• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind;
2 
3 import com.fasterxml.jackson.databind.cfg.MapperConfig;
4 import com.fasterxml.jackson.databind.introspect.AnnotatedField;
5 import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
6 import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
7 
8 /**
9  * Class that defines how names of JSON properties ("external names")
10  * are derived from names of POJO methods and fields ("internal names"),
11  * in cases where they are not
12  * auto-detected and no explicit annotations exist for naming.
13  * Methods are passed information about POJO member for which name is needed,
14  * as well as default name that would be used if no custom strategy was used.
15  *<p>
16  * Default (empty) implementation returns suggested ("default") name unmodified.
17  *<p>
18  * Note that the strategy is guaranteed to be called once per logical property
19  * (which may be represented by multiple members; such as pair of a getter and
20  * a setter), but may be called for each: implementations should not count on
21  * exact number of times, and should work for any member that represent a
22  * property.
23  *<p>
24  * In absence of a registered custom strategy, default Java property naming strategy
25  * is used, which leaves field names as is, and removes set/get/is prefix
26  * from methods (as well as lower-cases initial sequence of capitalized
27  * characters).
28  */
29 @SuppressWarnings("serial")
30 public class PropertyNamingStrategy // NOTE: was abstract until 2.7
31     implements java.io.Serializable
32 {
33     /**
34      * Naming convention used in languages like C, where words are in lower-case
35      * letters, separated by underscores.
36      * See {@link SnakeCaseStrategy} for details.
37      *
38      * @since 2.7 (was formerly called {@link #CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES})
39      */
40     public static final PropertyNamingStrategy SNAKE_CASE = new SnakeCaseStrategy();
41 
42     /**
43      * Naming convention used in languages like Pascal, where words are capitalized
44      * and no separator is used between words.
45      * See {@link PascalCaseStrategy} for details.
46      *
47      * @since 2.7 (was formerly called {@link #PASCAL_CASE_TO_CAMEL_CASE})
48      */
49     public static final PropertyNamingStrategy UPPER_CAMEL_CASE = new UpperCamelCaseStrategy();
50 
51     /**
52      * Naming convention used in Java, where words other than first are capitalized
53      * and no separator is used between words. Since this is the native Java naming convention,
54      * naming strategy will not do any transformation between names in data (JSON) and
55      * POJOS.
56      *
57      * @since 2.7 (was formerly called {@link #PASCAL_CASE_TO_CAMEL_CASE})
58      */
59     public static final PropertyNamingStrategy LOWER_CAMEL_CASE = new PropertyNamingStrategy();
60 
61     /**
62      * Naming convention in which all words of the logical name are in lower case, and
63      * no separator is used between words.
64      * See {@link LowerCaseStrategy} for details.
65      *
66      * @since 2.4
67      */
68     public static final PropertyNamingStrategy LOWER_CASE = new LowerCaseStrategy();
69 
70     /**
71      * Naming convention used in languages like Lisp, where words are in lower-case
72      * letters, separated by hyphens.
73      * See {@link KebabCaseStrategy} for details.
74      *
75      * @since 2.7
76      */
77     public static final PropertyNamingStrategy KEBAB_CASE = new KebabCaseStrategy();
78 
79     /**
80      * Naming convention widely used as configuration properties name, where words are in
81      * lower-case letters, separated by dots.
82      * See {@link LowerDotCaseStrategy} for details.
83      *
84      * @since 2.10
85      */
86     public static final PropertyNamingStrategy LOWER_DOT_CASE = new LowerDotCaseStrategy();
87 
88     /*
89     /**********************************************************
90     /* API
91     /**********************************************************
92      */
93 
94     /**
95      * Method called to find external name (name used in JSON) for given logical
96      * POJO property,
97      * as defined by given field.
98      *
99      * @param config Configuration in used: either <code>SerializationConfig</code>
100      *   or <code>DeserializationConfig</code>, depending on whether method is called
101      *   during serialization or deserialization
102      * @param field Field used to access property
103      * @param defaultName Default name that would be used for property in absence of custom strategy
104      *
105      * @return Logical name to use for property that the field represents
106      */
nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName)107     public String nameForField(MapperConfig<?> config, AnnotatedField field,
108             String defaultName)
109     {
110         return defaultName;
111     }
112 
113     /**
114      * Method called to find external name (name used in JSON) for given logical
115      * POJO property,
116      * as defined by given getter method; typically called when building a serializer.
117      * (but not always -- when using "getter-as-setter", may be called during
118      * deserialization)
119      *
120      * @param config Configuration in used: either <code>SerializationConfig</code>
121      *   or <code>DeserializationConfig</code>, depending on whether method is called
122      *   during serialization or deserialization
123      * @param method Method used to access property.
124      * @param defaultName Default name that would be used for property in absence of custom strategy
125      *
126      * @return Logical name to use for property that the method represents
127      */
nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)128     public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method,
129             String defaultName)
130     {
131         return defaultName;
132     }
133 
134     /**
135      * Method called to find external name (name used in JSON) for given logical
136      * POJO property,
137      * as defined by given setter method; typically called when building a deserializer
138      * (but not necessarily only then).
139      *
140      * @param config Configuration in used: either <code>SerializationConfig</code>
141      *   or <code>DeserializationConfig</code>, depending on whether method is called
142      *   during serialization or deserialization
143      * @param method Method used to access property.
144      * @param defaultName Default name that would be used for property in absence of custom strategy
145      *
146      * @return Logical name to use for property that the method represents
147      */
nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)148     public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method,
149             String defaultName)
150     {
151         return defaultName;
152     }
153 
154     /**
155      * Method called to find external name (name used in JSON) for given logical
156      * POJO property,
157      * as defined by given constructor parameter; typically called when building a deserializer
158      * (but not necessarily only then).
159      *
160      * @param config Configuration in used: either <code>SerializationConfig</code>
161      *   or <code>DeserializationConfig</code>, depending on whether method is called
162      *   during serialization or deserialization
163      * @param ctorParam Constructor parameter used to pass property.
164      * @param defaultName Default name that would be used for property in absence of custom strategy
165      */
nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam, String defaultName)166     public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam,
167             String defaultName)
168     {
169         return defaultName;
170     }
171 
172     /*
173     /**********************************************************
174     /* Public base class for simple implementations
175     /**********************************************************
176      */
177 
178     public static abstract class PropertyNamingStrategyBase extends PropertyNamingStrategy
179     {
180         @Override
nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName)181         public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName)
182         {
183             return translate(defaultName);
184         }
185 
186         @Override
nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)187         public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
188         {
189             return translate(defaultName);
190         }
191 
192         @Override
nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)193         public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
194         {
195             return translate(defaultName);
196         }
197 
198         @Override
nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam, String defaultName)199         public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam,
200                 String defaultName)
201         {
202             return translate(defaultName);
203         }
204 
translate(String propertyName)205         public abstract String translate(String propertyName);
206 
207         /**
208          * Helper method to share implementation between snake and dotted case.
209          */
translateLowerCaseWithSeparator(final String input, final char separator)210         protected static String translateLowerCaseWithSeparator(final String input, final char separator)
211         {
212             if (input == null) {
213                 return input; // garbage in, garbage out
214             }
215             final int length = input.length();
216             if (length == 0) {
217                 return input;
218             }
219 
220             final StringBuilder result = new StringBuilder(length + (length >> 1));
221             int upperCount = 0;
222             for (int i = 0; i < length; ++i) {
223                 char ch = input.charAt(i);
224                 char lc = Character.toLowerCase(ch);
225 
226                 if (lc == ch) { // lower-case letter means we can get new word
227                     // but need to check for multi-letter upper-case (acronym), where assumption
228                     // is that the last upper-case char is start of a new word
229                     if (upperCount > 1) {
230                         // so insert hyphen before the last character now
231                         result.insert(result.length() - 1, separator);
232                     }
233                     upperCount = 0;
234                 } else {
235                     // Otherwise starts new word, unless beginning of string
236                     if ((upperCount == 0) && (i > 0)) {
237                         result.append(separator);
238                     }
239                     ++upperCount;
240                 }
241                 result.append(lc);
242             }
243             return result.toString();
244         }
245     }
246 
247     /*
248     /**********************************************************
249     /* Standard implementations
250     /**********************************************************
251      */
252 
253     /**
254      * A {@link PropertyNamingStrategy} that translates typical camel case Java
255      * property names to lower case JSON element names, separated by
256      * underscores.  This implementation is somewhat lenient, in that it
257      * provides some additional translations beyond strictly translating from
258      * camel case only.  In particular, the following translations are applied
259      * by this PropertyNamingStrategy.
260      *
261      * <ul><li>Every upper case letter in the Java property name is translated
262      * into two characters, an underscore and the lower case equivalent of the
263      * target character, with three exceptions.
264      * <ol><li>For contiguous sequences of upper case letters, characters after
265      * the first character are replaced only by their lower case equivalent,
266      * and are not preceded by an underscore.
267      * <ul><li>This provides for reasonable translations of upper case acronyms,
268      * e.g., &quot;theWWW&quot; is translated to &quot;the_www&quot;.</li></ul></li>
269      * <li>An upper case character in the first position of the Java property
270      * name is not preceded by an underscore character, and is translated only
271      * to its lower case equivalent.
272      * <ul><li>For example, &quot;Results&quot; is translated to &quot;results&quot;,
273      * and not to &quot;_results&quot;.</li></ul></li>
274      * <li>An upper case character in the Java property name that is already
275      * preceded by an underscore character is translated only to its lower case
276      * equivalent, and is not preceded by an additional underscore.
277      * <ul><li>For example, &quot;user_Name&quot; is translated to
278      * &quot;user_name&quot;, and not to &quot;user__name&quot; (with two
279      * underscore characters).</li></ul></li></ol></li>
280      * <li>If the Java property name starts with an underscore, then that
281      * underscore is not included in the translated name, unless the Java
282      * property name is just one character in length, i.e., it is the
283      * underscore character.  This applies only to the first character of the
284      * Java property name.</li></ul>
285      *
286      * These rules result in the following additional example translations from
287      * Java property names to JSON element names.
288      * <ul><li>&quot;userName&quot; is translated to &quot;user_name&quot;</li>
289      * <li>&quot;UserName&quot; is translated to &quot;user_name&quot;</li>
290      * <li>&quot;USER_NAME&quot; is translated to &quot;user_name&quot;</li>
291      * <li>&quot;user_name&quot; is translated to &quot;user_name&quot; (unchanged)</li>
292      * <li>&quot;user&quot; is translated to &quot;user&quot; (unchanged)</li>
293      * <li>&quot;User&quot; is translated to &quot;user&quot;</li>
294      * <li>&quot;USER&quot; is translated to &quot;user&quot;</li>
295      * <li>&quot;_user&quot; is translated to &quot;user&quot;</li>
296      * <li>&quot;_User&quot; is translated to &quot;user&quot;</li>
297      * <li>&quot;__user&quot; is translated to &quot;_user&quot;
298      * (the first of two underscores was removed)</li>
299      * <li>&quot;user__name&quot; is translated to &quot;user__name&quot;
300      * (unchanged, with two underscores)</li></ul>
301      *
302      * @since 2.7 (was previously called {@link LowerCaseWithUnderscoresStrategy})
303      */
304     public static class SnakeCaseStrategy extends PropertyNamingStrategyBase
305     {
306         @Override
translate(String input)307         public String translate(String input)
308         {
309             if (input == null) return input; // garbage in, garbage out
310             int length = input.length();
311             StringBuilder result = new StringBuilder(length * 2);
312             int resultLength = 0;
313             boolean wasPrevTranslated = false;
314             for (int i = 0; i < length; i++)
315             {
316                 char c = input.charAt(i);
317                 if (i > 0 || c != '_') // skip first starting underscore
318                 {
319                     if (Character.isUpperCase(c))
320                     {
321                         if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
322                         {
323                             result.append('_');
324                             resultLength++;
325                         }
326                         c = Character.toLowerCase(c);
327                         wasPrevTranslated = true;
328                     }
329                     else
330                     {
331                         wasPrevTranslated = false;
332                     }
333                     result.append(c);
334                     resultLength++;
335                 }
336             }
337             return resultLength > 0 ? result.toString() : input;
338         }
339     }
340 
341     /**
342      * A {@link PropertyNamingStrategy} that translates typical camelCase Java
343      * property names to PascalCase JSON element names (i.e., with a capital
344      * first letter).  In particular, the following translations are applied by
345      * this PropertyNamingStrategy.
346      *
347      * <ul><li>The first lower-case letter in the Java property name is translated
348      * into its equivalent upper-case representation.</li></ul>
349      *
350      * This rules result in the following example translation from
351      * Java property names to JSON element names.
352      * <ul><li>&quot;userName&quot; is translated to &quot;UserName&quot;</li></ul>
353      *
354      * @since 2.7 (was formerly called {@link PascalCaseStrategy})
355      */
356     public static class UpperCamelCaseStrategy extends PropertyNamingStrategyBase
357     {
358         /**
359          * Converts camelCase to PascalCase
360          *
361          * For example, "userName" would be converted to
362          * "UserName".
363          *
364          * @param input formatted as camelCase string
365          * @return input converted to PascalCase format
366          */
367         @Override
translate(String input)368         public String translate(String input) {
369             if (input == null || input.length() == 0){
370                 return input; // garbage in, garbage out
371             }
372             // Replace first lower-case letter with upper-case equivalent
373             char c = input.charAt(0);
374             char uc = Character.toUpperCase(c);
375             if (c == uc) {
376                 return input;
377             }
378             StringBuilder sb = new StringBuilder(input);
379             sb.setCharAt(0, uc);
380             return sb.toString();
381         }
382     }
383 
384     /**
385      * Simple strategy where external name simply only uses lower-case characters,
386      * and no separators.
387      * Conversion from internal name like "someOtherValue" would be into external name
388      * if "someothervalue".
389      *
390      * @since 2.4
391      */
392     public static class LowerCaseStrategy extends PropertyNamingStrategyBase
393     {
394         @Override
translate(String input)395         public String translate(String input) {
396             return input.toLowerCase();
397         }
398     }
399 
400     /**
401      * Naming strategy similar to {@link SnakeCaseStrategy}, but instead of underscores
402      * as separators, uses hyphens. Naming convention traditionally used for languages
403      * like Lisp.
404      *
405      * @since 2.7
406      */
407     public static class KebabCaseStrategy extends PropertyNamingStrategyBase
408     {
409         @Override
translate(String input)410         public String translate(String input) {
411             return translateLowerCaseWithSeparator(input, '-');
412         }
413     }
414 
415     /**
416      * Naming strategy similar to {@link KebabCaseStrategy}, but instead of hyphens
417      * as separators, uses dots. Naming convention widely used as configuration properties name.
418      *
419      * @since 2.10
420      */
421     public static class LowerDotCaseStrategy extends PropertyNamingStrategyBase {
422         @Override
translate(String input)423         public String translate(String input){
424             return translateLowerCaseWithSeparator(input, '.');
425         }
426     }
427 
428     /*
429     /**********************************************************
430     /* Deprecated variants, aliases
431     /**********************************************************
432      */
433 
434     /**
435      * @deprecated Since 2.7 use {@link #SNAKE_CASE} instead;
436      */
437     @Deprecated // since 2.7
438     public static final PropertyNamingStrategy CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES = SNAKE_CASE;
439 
440     /**
441      * @deprecated Since 2.7 use {@link #UPPER_CAMEL_CASE} instead;
442      */
443     @Deprecated // since 2.7
444     public static final PropertyNamingStrategy PASCAL_CASE_TO_CAMEL_CASE = UPPER_CAMEL_CASE;
445 
446     /**
447      * @deprecated In 2.7 use {@link SnakeCaseStrategy} instead
448      */
449     @Deprecated
450     public static class LowerCaseWithUnderscoresStrategy extends SnakeCaseStrategy {}
451 
452     /**
453      * @deprecated In 2.7 use {@link UpperCamelCaseStrategy} instead
454      */
455     @Deprecated
456     public static class PascalCaseStrategy extends UpperCamelCaseStrategy {}
457 }
458 
459