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., "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 {@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>"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 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