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 */
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 */
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 */
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 */
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
181 public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName)
182 {
183 return translate(defaultName);
184 }
185
186 @Override
187 public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
188 {
189 return translate(defaultName);
190 }
191
192 @Override
193 public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
194 {
195 return translate(defaultName);
196 }
197
198 @Override
199 public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam,
200 String defaultName)
201 {
202 return translate(defaultName);
203 }
204
205 public abstract String translate(String propertyName);
206
207 /**
208 * Helper method to share implementation between snake and dotted case.
209 */
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., "theWWW" is translated to "the_www".</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, "Results" is translated to "results",
273 * and not to "_results".</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, "user_Name" is translated to
278 * "user_name", and not to "user__name" (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>"userName" is translated to "user_name"</li>
289 * <li>"UserName" is translated to "user_name"</li>
290 * <li>"USER_NAME" is translated to "user_name"</li>
291 * <li>"user_name" is translated to "user_name" (unchanged)</li>
292 * <li>"user" is translated to "user" (unchanged)</li>
293 * <li>"User" is translated to "user"</li>
294 * <li>"USER" is translated to "user"</li>
295 * <li>"_user" is translated to "user"</li>
296 * <li>"_User" is translated to "user"</li>
297 * <li>"__user" is translated to "_user"
298 * (the first of two underscores was removed)</li>
299 * <li>"user__name" is translated to "user__name"
300 * (unchanged, with two underscores)</li></ul>
301 *
302 * @since 2.7 (was previously called }
303 */
304 public static class SnakeCaseStrategy extends PropertyNamingStrategyBase
305 {
306 @Override
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>"userName" is translated to "UserName"</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
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
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
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
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