1 package com.fasterxml.jackson.databind.introspect; 2 3 import java.util.Arrays; 4 import java.util.List; 5 6 import com.fasterxml.jackson.annotation.*; 7 8 import com.fasterxml.jackson.databind.*; 9 import com.fasterxml.jackson.databind.annotation.JsonNaming; 10 import com.fasterxml.jackson.databind.introspect.TestNamingStrategyCustom.PersonBean; 11 import com.fasterxml.jackson.databind.node.ObjectNode; 12 13 /** 14 * Unit tests to verify functioning of standard {@link PropertyNamingStrategy} 15 * implementations Jackson includes out of the box. 16 */ 17 public class TestNamingStrategyStd extends BaseMapTest 18 { 19 @JsonPropertyOrder({"www", "some_url", "some_uris"}) 20 static class Acronyms 21 { 22 public String WWW; 23 public String someURL; 24 public String someURIs; 25 Acronyms()26 public Acronyms() {this(null, null, null);} Acronyms(String WWW, String someURL, String someURIs)27 public Acronyms(String WWW, String someURL, String someURIs) 28 { 29 this.WWW = WWW; 30 this.someURL = someURL; 31 this.someURIs = someURIs; 32 } 33 } 34 35 @JsonPropertyOrder({"from_user", "user", "from$user", "from7user", "_x"}) 36 static class UnchangedNames 37 { 38 public String from_user; 39 public String _user; 40 public String from$user; 41 public String from7user; 42 // Used to test "_", but it's explicitly deprecated in JDK8 so... 43 public String _x; 44 UnchangedNames()45 public UnchangedNames() {this(null, null, null, null, null);} UnchangedNames(String from_user, String _user, String from$user, String from7user, String _x)46 public UnchangedNames(String from_user, String _user, String from$user, String from7user, String _x) 47 { 48 this.from_user = from_user; 49 this._user = _user; 50 this.from$user = from$user; 51 this.from7user = from7user; 52 this._x = _x; 53 } 54 } 55 56 @JsonPropertyOrder({"results", "user", "__", "$_user"}) 57 static class OtherNonStandardNames 58 { 59 public String Results; 60 public String _User; 61 public String ___; 62 public String $User; 63 OtherNonStandardNames()64 public OtherNonStandardNames() {this(null, null, null, null);} OtherNonStandardNames(String Results, String _User, String ___, String $User)65 public OtherNonStandardNames(String Results, String _User, String ___, String $User) 66 { 67 this.Results = Results; 68 this._User = _User; 69 this.___ = ___; 70 this.$User = $User; 71 } 72 } 73 74 static class Bean428 { 75 @JsonProperty("fooBar") whatever()76 public String whatever() {return "";} 77 } 78 79 @JsonPropertyOrder({ "firstName", "lastName" }) 80 @JsonNaming(PropertyNamingStrategy.LowerCaseStrategy.class) 81 static class BoringBean { 82 public String firstName = "Bob"; 83 public String lastName = "Burger"; 84 } 85 86 public static class ClassWithObjectNodeField { 87 public String id; 88 public ObjectNode json; 89 } 90 91 static class ExplicitBean { 92 @JsonProperty("firstName") 93 String userFirstName = "Peter"; 94 @JsonProperty("lastName") 95 String userLastName = "Venkman"; 96 @JsonProperty 97 String userAge = "35"; 98 } 99 100 @JsonNaming() 101 static class DefaultNaming { 102 public int someValue = 3; 103 } 104 105 static class FirstNameBean { 106 public String firstName; 107 FirstNameBean()108 protected FirstNameBean() { } FirstNameBean(String n)109 public FirstNameBean(String n) { firstName = n; } 110 } 111 112 /* 113 /********************************************************** 114 /* Set up 115 /********************************************************** 116 */ 117 118 final static List<Object[]> SNAKE_CASE_NAME_TRANSLATIONS = Arrays.asList(new Object[][] { 119 {null, null}, 120 {"", ""}, 121 {"a", "a"}, 122 {"abc", "abc"}, 123 {"1", "1"}, 124 {"123", "123"}, 125 {"1a", "1a"}, 126 {"a1", "a1"}, 127 {"$", "$"}, 128 {"$a", "$a"}, 129 {"a$", "a$"}, 130 {"$_a", "$_a"}, 131 {"a_$", "a_$"}, 132 {"a$a", "a$a"}, 133 {"$A", "$_a"}, 134 {"$_A", "$_a"}, 135 {"_", "_"}, 136 {"__", "_"}, 137 {"___", "__"}, 138 {"A", "a"}, 139 {"A1", "a1"}, 140 {"1A", "1_a"}, 141 {"_a", "a"}, 142 {"_A", "a"}, 143 {"a_a", "a_a"}, 144 {"a_A", "a_a"}, 145 {"A_A", "a_a"}, 146 {"A_a", "a_a"}, 147 {"WWW", "www"}, 148 {"someURI", "some_uri"}, 149 {"someURIs", "some_uris"}, 150 {"Results", "results"}, 151 {"_Results", "results"}, 152 {"_results", "results"}, 153 {"__results", "_results"}, 154 {"__Results", "_results"}, 155 {"___results", "__results"}, 156 {"___Results", "__results"}, 157 {"userName", "user_name"}, 158 {"user_name", "user_name"}, 159 {"user__name", "user__name"}, 160 {"UserName", "user_name"}, 161 {"User_Name", "user_name"}, 162 {"User__Name", "user__name"}, 163 {"_user_name", "user_name"}, 164 {"_UserName", "user_name"}, 165 {"_User_Name", "user_name"}, 166 {"UGLY_NAME", "ugly_name"}, 167 {"_Bars", "bars" }, 168 // [databind#1026] 169 {"usId", "us_id" }, 170 {"uId", "u_id" }, 171 // [databind#2267] 172 {"xCoordinate", "x_coordinate" }, 173 }); 174 175 private ObjectMapper _lcWithUndescoreMapper; 176 177 @Override setUp()178 public void setUp() throws Exception 179 { 180 super.setUp(); 181 _lcWithUndescoreMapper = new ObjectMapper(); 182 _lcWithUndescoreMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); 183 } 184 185 /* 186 /********************************************************** 187 /* Test methods for SNAKE_CASE 188 /********************************************************** 189 */ 190 191 /** 192 * Unit test to verify translations of 193 * {@link PropertyNamingStrategy#SNAKE_CASE} 194 * outside the context of an ObjectMapper. 195 */ testLowerCaseStrategyStandAlone()196 public void testLowerCaseStrategyStandAlone() 197 { 198 for (Object[] pair : SNAKE_CASE_NAME_TRANSLATIONS) { 199 String translatedJavaName = PropertyNamingStrategy.SNAKE_CASE.nameForField(null, null, 200 (String) pair[0]); 201 assertEquals((String) pair[1], translatedJavaName); 202 } 203 } 204 testLowerCaseTranslations()205 public void testLowerCaseTranslations() throws Exception 206 { 207 // First serialize 208 String json = _lcWithUndescoreMapper.writeValueAsString(new PersonBean("Joe", "Sixpack", 42)); 209 assertEquals("{\"first_name\":\"Joe\",\"last_name\":\"Sixpack\",\"age\":42}", json); 210 211 // then deserialize 212 PersonBean result = _lcWithUndescoreMapper.readValue(json, PersonBean.class); 213 assertEquals("Joe", result.firstName); 214 assertEquals("Sixpack", result.lastName); 215 assertEquals(42, result.age); 216 } 217 testLowerCaseAcronymsTranslations()218 public void testLowerCaseAcronymsTranslations() throws Exception 219 { 220 // First serialize 221 String json = _lcWithUndescoreMapper.writeValueAsString(new Acronyms("world wide web", "http://jackson.codehaus.org", "/path1/,/path2/")); 222 assertEquals("{\"www\":\"world wide web\",\"some_url\":\"http://jackson.codehaus.org\",\"some_uris\":\"/path1/,/path2/\"}", json); 223 224 // then deserialize 225 Acronyms result = _lcWithUndescoreMapper.readValue(json, Acronyms.class); 226 assertEquals("world wide web", result.WWW); 227 assertEquals("http://jackson.codehaus.org", result.someURL); 228 assertEquals("/path1/,/path2/", result.someURIs); 229 } 230 testLowerCaseOtherNonStandardNamesTranslations()231 public void testLowerCaseOtherNonStandardNamesTranslations() throws Exception 232 { 233 // First serialize 234 String json = _lcWithUndescoreMapper.writeValueAsString(new OtherNonStandardNames("Results", "_User", "___", "$User")); 235 assertEquals("{\"results\":\"Results\",\"user\":\"_User\",\"__\":\"___\",\"$_user\":\"$User\"}", json); 236 237 // then deserialize 238 OtherNonStandardNames result = _lcWithUndescoreMapper.readValue(json, OtherNonStandardNames.class); 239 assertEquals("Results", result.Results); 240 assertEquals("_User", result._User); 241 assertEquals("___", result.___); 242 assertEquals("$User", result.$User); 243 } 244 testLowerCaseUnchangedNames()245 public void testLowerCaseUnchangedNames() throws Exception 246 { 247 // First serialize 248 String json = _lcWithUndescoreMapper.writeValueAsString(new UnchangedNames("from_user", "_user", "from$user", "from7user", "_x")); 249 assertEquals("{\"from_user\":\"from_user\",\"user\":\"_user\",\"from$user\":\"from$user\",\"from7user\":\"from7user\",\"x\":\"_x\"}", json); 250 251 // then deserialize 252 UnchangedNames result = _lcWithUndescoreMapper.readValue(json, UnchangedNames.class); 253 assertEquals("from_user", result.from_user); 254 assertEquals("_user", result._user); 255 assertEquals("from$user", result.from$user); 256 assertEquals("from7user", result.from7user); 257 assertEquals("_x", result._x); 258 } 259 260 /* 261 /********************************************************** 262 /* Test methods for UPPER_CAMEL_CASE 263 /********************************************************** 264 */ 265 266 /** 267 * Unit test to verify translations of 268 * {@link PropertyNamingStrategy#UPPER_CAMEL_CASE } 269 * outside the context of an ObjectMapper. 270 */ testPascalCaseStandAlone()271 public void testPascalCaseStandAlone() 272 { 273 assertEquals("UserName", PropertyNamingStrategy.UPPER_CAMEL_CASE.nameForField(null, null, "userName")); 274 assertEquals("User", PropertyNamingStrategy.UPPER_CAMEL_CASE.nameForField(null, null, "User")); 275 assertEquals("User", PropertyNamingStrategy.UPPER_CAMEL_CASE.nameForField(null, null, "user")); 276 assertEquals("X", PropertyNamingStrategy.UPPER_CAMEL_CASE.nameForField(null, null, "x")); 277 278 assertEquals("BADPublicName", PropertyNamingStrategy.UPPER_CAMEL_CASE.nameForField(null, null, "bADPublicName")); 279 assertEquals("BADPublicName", PropertyNamingStrategy.UPPER_CAMEL_CASE.nameForGetterMethod(null, null, "bADPublicName")); 280 } 281 282 // [databind#428] testIssue428PascalWithOverrides()283 public void testIssue428PascalWithOverrides() throws Exception 284 { 285 String json = new ObjectMapper() 286 .setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE) 287 .writeValueAsString(new Bean428()); 288 if (!json.contains(quote("fooBar"))) { 289 fail("Should use name 'fooBar', does not: "+json); 290 } 291 } 292 293 /* 294 /********************************************************** 295 /* Test methods for LOWER_CASE 296 /********************************************************** 297 */ 298 299 // For [databind#461] testSimpleLowerCase()300 public void testSimpleLowerCase() throws Exception 301 { 302 final BoringBean input = new BoringBean(); 303 ObjectMapper m = objectMapper(); 304 305 assertEquals(aposToQuotes("{'firstname':'Bob','lastname':'Burger'}"), 306 m.writeValueAsString(input)); 307 } 308 309 /* 310 /********************************************************** 311 /* Test methods for KEBAB_CASE 312 /********************************************************** 313 */ 314 testKebabCaseStrategyStandAlone()315 public void testKebabCaseStrategyStandAlone() 316 { 317 assertEquals("some-value", 318 PropertyNamingStrategy.KEBAB_CASE.nameForField(null, null, "someValue")); 319 assertEquals("some-value", 320 PropertyNamingStrategy.KEBAB_CASE.nameForField(null, null, "SomeValue")); 321 assertEquals("url", 322 PropertyNamingStrategy.KEBAB_CASE.nameForField(null, null, "URL")); 323 assertEquals("url-stuff", 324 PropertyNamingStrategy.KEBAB_CASE.nameForField(null, null, "URLStuff")); 325 assertEquals("some-url-stuff", 326 PropertyNamingStrategy.KEBAB_CASE.nameForField(null, null, "SomeURLStuff")); 327 } 328 testSimpleKebabCase()329 public void testSimpleKebabCase() throws Exception 330 { 331 final FirstNameBean input = new FirstNameBean("Bob"); 332 ObjectMapper m = new ObjectMapper() 333 .setPropertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE); 334 335 assertEquals(aposToQuotes("{'first-name':'Bob'}"), m.writeValueAsString(input)); 336 337 FirstNameBean result = m.readValue(aposToQuotes("{'first-name':'Billy'}"), 338 FirstNameBean.class); 339 assertEquals("Billy", result.firstName); 340 } 341 /* 342 /********************************************************** 343 /* Test methods for LOWER_DOT_CASE 344 /********************************************************** 345 */ 346 testLowerCaseWithDotsStrategyStandAlone()347 public void testLowerCaseWithDotsStrategyStandAlone() 348 { 349 assertEquals("some.value", 350 PropertyNamingStrategy.LOWER_DOT_CASE.nameForField(null, null, "someValue")); 351 assertEquals("some.value", 352 PropertyNamingStrategy.LOWER_DOT_CASE.nameForField(null, null, "SomeValue")); 353 assertEquals("url", 354 PropertyNamingStrategy.LOWER_DOT_CASE.nameForField(null, null, "URL")); 355 assertEquals("url.stuff", 356 PropertyNamingStrategy.LOWER_DOT_CASE.nameForField(null, null, "URLStuff")); 357 assertEquals("some.url.stuff", 358 PropertyNamingStrategy.LOWER_DOT_CASE.nameForField(null, null, "SomeURLStuff")); 359 } 360 testSimpleLowerCaseWithDots()361 public void testSimpleLowerCaseWithDots() throws Exception 362 { 363 final ObjectMapper m = jsonMapperBuilder() 364 .propertyNamingStrategy(PropertyNamingStrategy.LOWER_DOT_CASE) 365 .build(); 366 367 final FirstNameBean input = new FirstNameBean("Bob"); 368 assertEquals(aposToQuotes("{'first.name':'Bob'}"), m.writeValueAsString(input)); 369 370 FirstNameBean result = m.readValue(aposToQuotes("{'first.name':'Billy'}"), 371 FirstNameBean.class); 372 assertEquals("Billy", result.firstName); 373 } 374 375 /* 376 /********************************************************** 377 /* Test methods, other 378 /********************************************************** 379 */ 380 381 /** 382 * Test [databind#815], problems with ObjectNode, naming strategy 383 */ testNamingWithObjectNode()384 public void testNamingWithObjectNode() throws Exception 385 { 386 ObjectMapper m = new ObjectMapper() 387 .setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE); 388 ClassWithObjectNodeField result = 389 m.readValue( 390 "{ \"id\": \"1\", \"json\": { \"foo\": \"bar\", \"baz\": \"bing\" } }", 391 ClassWithObjectNodeField.class); 392 assertNotNull(result); 393 assertEquals("1", result.id); 394 assertNotNull(result.json); 395 assertEquals(2, result.json.size()); 396 assertEquals("bing", result.json.path("baz").asText()); 397 } 398 testExplicitRename()399 public void testExplicitRename() throws Exception 400 { 401 ObjectMapper m = jsonMapperBuilder() 402 .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) 403 .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) 404 .build(); 405 // by default, renaming will not take place on explicitly named fields 406 assertEquals(aposToQuotes("{'firstName':'Peter','lastName':'Venkman','user_age':'35'}"), 407 m.writeValueAsString(new ExplicitBean())); 408 409 m = jsonMapperBuilder() 410 .propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE) 411 .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) 412 .enable(MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING) 413 .build(); 414 // w/ feature enabled, ALL property names should get re-written 415 assertEquals(aposToQuotes("{'first_name':'Peter','last_name':'Venkman','user_age':'35'}"), 416 m.writeValueAsString(new ExplicitBean())); 417 418 // test deserialization as well 419 ExplicitBean bean = 420 m.readValue(aposToQuotes("{'first_name':'Egon','last_name':'Spengler','user_age':'32'}"), 421 ExplicitBean.class); 422 423 assertNotNull(bean); 424 assertEquals("Egon", bean.userFirstName); 425 assertEquals("Spengler", bean.userLastName); 426 assertEquals("32", bean.userAge); 427 } 428 429 // Also verify that "no naming strategy" should be ok testExplicitNoNaming()430 public void testExplicitNoNaming() throws Exception 431 { 432 ObjectMapper mapper = objectMapper(); 433 String json = mapper.writeValueAsString(new DefaultNaming()); 434 assertEquals(aposToQuotes("{'someValue':3}"), json); 435 } 436 } 437