1 package com.fasterxml.jackson.databind.deser; 2 3 import java.io.IOException; 4 import java.util.*; 5 6 import com.fasterxml.jackson.annotation.JsonCreator; 7 import com.fasterxml.jackson.annotation.JsonProperty; 8 import com.fasterxml.jackson.core.*; 9 import com.fasterxml.jackson.databind.*; 10 import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 11 import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; 12 import com.fasterxml.jackson.databind.module.SimpleModule; 13 import com.fasterxml.jackson.databind.type.ArrayType; 14 import com.fasterxml.jackson.databind.type.CollectionType; 15 import com.fasterxml.jackson.databind.type.MapType; 16 17 @SuppressWarnings("serial") 18 public class TestBeanDeserializer extends BaseMapTest 19 { 20 static abstract class Abstract { 21 public int x; 22 } 23 24 static class Bean { 25 public String b = "b"; 26 public String a = "a"; 27 Bean()28 public Bean() { } Bean(String a, String b)29 public Bean(String a, String b) { 30 this.a = a; 31 this.b = b; 32 } 33 } 34 35 static class ModuleImpl extends SimpleModule 36 { 37 protected BeanDeserializerModifier modifier; 38 ModuleImpl(BeanDeserializerModifier modifier)39 public ModuleImpl(BeanDeserializerModifier modifier) 40 { 41 super("test", Version.unknownVersion()); 42 this.modifier = modifier; 43 } 44 45 @Override setupModule(SetupContext context)46 public void setupModule(SetupContext context) 47 { 48 super.setupModule(context); 49 if (modifier != null) { 50 context.addBeanDeserializerModifier(modifier); 51 } 52 } 53 } 54 55 static class RemovingModifier extends BeanDeserializerModifier 56 { 57 private final String _removedProperty; 58 RemovingModifier(String remove)59 public RemovingModifier(String remove) { _removedProperty = remove; } 60 61 @Override updateBuilder(DeserializationConfig config, BeanDescription beanDesc, BeanDeserializerBuilder builder)62 public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, 63 BeanDescription beanDesc, BeanDeserializerBuilder builder) { 64 builder.addIgnorable(_removedProperty); 65 return builder; 66 } 67 } 68 69 static class ReplacingModifier extends BeanDeserializerModifier 70 { 71 private final JsonDeserializer<?> _deserializer; 72 ReplacingModifier(JsonDeserializer<?> s)73 public ReplacingModifier(JsonDeserializer<?> s) { _deserializer = s; } 74 75 @Override modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer)76 public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, 77 BeanDescription beanDesc, JsonDeserializer<?> deserializer) { 78 return _deserializer; 79 } 80 } 81 82 static class BogusBeanDeserializer extends JsonDeserializer<Object> 83 { 84 private final String a, b; 85 BogusBeanDeserializer(String a, String b)86 public BogusBeanDeserializer(String a, String b) { 87 this.a = a; 88 this.b = b; 89 } 90 91 @Override deserialize(JsonParser jp, DeserializationContext ctxt)92 public Object deserialize(JsonParser jp, DeserializationContext ctxt) 93 throws IOException, JsonProcessingException 94 { 95 return new Bean(a, b); 96 } 97 } 98 static class Issue476Bean { 99 public Issue476Type value1, value2; 100 } 101 static class Issue476Type { 102 public String name, value; 103 } 104 static class Issue476Deserializer extends BeanDeserializer 105 implements ContextualDeserializer 106 { 107 protected static int propCount; 108 Issue476Deserializer(BeanDeserializer src)109 public Issue476Deserializer(BeanDeserializer src) { 110 super(src); 111 } 112 113 @Override createContextual(DeserializationContext ctxt, BeanProperty property)114 public JsonDeserializer<?> createContextual(DeserializationContext ctxt, 115 BeanProperty property) throws JsonMappingException { 116 propCount++; 117 return this; 118 } 119 } 120 public class Issue476DeserializerModifier extends BeanDeserializerModifier { 121 @Override modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer)122 public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, 123 BeanDescription beanDesc, JsonDeserializer<?> deserializer) { 124 if (Issue476Type.class == beanDesc.getBeanClass()) { 125 return new Issue476Deserializer((BeanDeserializer)deserializer); 126 } 127 return super.modifyDeserializer(config, beanDesc, deserializer); 128 } 129 } 130 public class Issue476Module extends SimpleModule 131 { Issue476Module()132 public Issue476Module() { 133 super("Issue476Module", Version.unknownVersion()); 134 } 135 136 @Override setupModule(SetupContext context)137 public void setupModule(SetupContext context) { 138 context.addBeanDeserializerModifier(new Issue476DeserializerModifier()); 139 } 140 } 141 142 public static class Issue1912Bean { 143 public Issue1912SubBean subBean; 144 145 @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) // This is need to populate _propertyBasedCreator on BeanDeserializerBase Issue1912Bean(@sonProperty"subBean") Issue1912SubBean subBean)146 public Issue1912Bean(@JsonProperty("subBean") Issue1912SubBean subBean) { 147 this.subBean = subBean; 148 } 149 } 150 public static class Issue1912SubBean { 151 public String a; 152 Issue1912SubBean()153 public Issue1912SubBean() { } 154 Issue1912SubBean(String a)155 public Issue1912SubBean(String a) { 156 this.a = a; 157 } 158 } 159 160 public static class Issue1912CustomBeanDeserializer extends JsonDeserializer<Issue1912Bean> { 161 private BeanDeserializer defaultDeserializer; 162 Issue1912CustomBeanDeserializer(BeanDeserializer defaultDeserializer)163 public Issue1912CustomBeanDeserializer(BeanDeserializer defaultDeserializer) { 164 this.defaultDeserializer = defaultDeserializer; 165 } 166 167 @Override deserialize(JsonParser p, DeserializationContext ctxt)168 public Issue1912Bean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 169 // this is need on some cases, this populate _propertyBasedCreator 170 defaultDeserializer.resolve(ctxt); 171 172 p.nextFieldName(); // read subBean 173 p.nextToken(); // read start object 174 175 Issue1912SubBean subBean = (Issue1912SubBean) defaultDeserializer.findProperty("subBean").deserialize(p, ctxt); 176 177 return new Issue1912Bean(subBean); 178 } 179 } 180 181 public static class Issue1912CustomPropertyDeserializer extends JsonDeserializer<Issue1912SubBean> { 182 183 @Override deserialize(JsonParser p, DeserializationContext ctxt)184 public Issue1912SubBean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 185 p.nextFieldName(); // read "a" 186 Issue1912SubBean object = new Issue1912SubBean(p.nextTextValue() + "_custom"); 187 p.nextToken(); 188 return object; 189 } 190 } 191 public static class Issue1912UseAddOrReplacePropertyDeserializerModifier extends BeanDeserializerModifier { 192 193 @Override modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer)194 public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) { 195 if (beanDesc.getBeanClass() == Issue1912Bean.class) { 196 return new Issue1912CustomBeanDeserializer((BeanDeserializer) deserializer); 197 } 198 return super.modifyDeserializer(config, beanDesc, deserializer); 199 } 200 201 @Override updateBuilder(DeserializationConfig config, BeanDescription beanDesc, BeanDeserializerBuilder builder)202 public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc, BeanDeserializerBuilder builder) { 203 if (beanDesc.getBeanClass() == Issue1912Bean.class) { 204 Iterator<SettableBeanProperty> props = builder.getProperties(); 205 while (props.hasNext()) { 206 SettableBeanProperty prop = props.next(); 207 SettableBeanProperty propWithCustomDeserializer = prop.withValueDeserializer(new Issue1912CustomPropertyDeserializer()); 208 builder.addOrReplaceProperty(propWithCustomDeserializer, true); 209 } 210 } 211 212 return builder; 213 } 214 } 215 public class Issue1912Module extends SimpleModule { 216 Issue1912Module()217 public Issue1912Module() { 218 super("Issue1912Module", Version.unknownVersion()); 219 } 220 221 @Override setupModule(SetupContext context)222 public void setupModule(SetupContext context) { 223 context.addBeanDeserializerModifier(new Issue1912UseAddOrReplacePropertyDeserializerModifier()); 224 } 225 } 226 227 // [Issue#121], arrays, collections, maps 228 229 enum EnumABC { A, B, C; } 230 231 static class ArrayDeserializerModifier extends BeanDeserializerModifier { 232 @Override modifyArrayDeserializer(DeserializationConfig config, ArrayType valueType, BeanDescription beanDesc, JsonDeserializer<?> deserializer)233 public JsonDeserializer<?> modifyArrayDeserializer(DeserializationConfig config, ArrayType valueType, 234 BeanDescription beanDesc, JsonDeserializer<?> deserializer) { 235 return (JsonDeserializer<?>) new StdDeserializer<Object>(Object.class) { 236 @Override public Object deserialize(JsonParser jp, 237 DeserializationContext ctxt) { 238 return new String[] { "foo" }; 239 } 240 }; 241 } 242 } 243 244 static class CollectionDeserializerModifier extends BeanDeserializerModifier { 245 @Override 246 public JsonDeserializer<?> modifyCollectionDeserializer(DeserializationConfig config, CollectionType valueType, 247 BeanDescription beanDesc, JsonDeserializer<?> deserializer) { 248 return (JsonDeserializer<?>) new StdDeserializer<Object>(Object.class) { 249 @Override public Object deserialize(JsonParser jp, 250 DeserializationContext ctxt) { 251 ArrayList<String> list = new ArrayList<String>(); 252 list.add("foo"); 253 return list; 254 } 255 }; 256 } 257 } 258 259 static class MapDeserializerModifier extends BeanDeserializerModifier { 260 @Override 261 public JsonDeserializer<?> modifyMapDeserializer(DeserializationConfig config, MapType valueType, 262 BeanDescription beanDesc, JsonDeserializer<?> deserializer) { 263 return (JsonDeserializer<?>) new StdDeserializer<Object>(Object.class) { 264 @Override public Object deserialize(JsonParser jp, 265 DeserializationContext ctxt) { 266 HashMap<String,String> map = new HashMap<String,String>(); 267 map.put("a", "foo"); 268 return map; 269 } 270 }; 271 } 272 } 273 274 static class EnumDeserializerModifier extends BeanDeserializerModifier { 275 @Override 276 public JsonDeserializer<?> modifyEnumDeserializer(DeserializationConfig config, JavaType valueType, 277 BeanDescription beanDesc, JsonDeserializer<?> deserializer) { 278 return (JsonDeserializer<?>) new StdDeserializer<Object>(Object.class) { 279 @Override public Object deserialize(JsonParser jp, 280 DeserializationContext ctxt) { 281 return "foo"; 282 } 283 }; 284 } 285 } 286 287 static class KeyDeserializerModifier extends BeanDeserializerModifier { 288 @Override 289 public KeyDeserializer modifyKeyDeserializer(DeserializationConfig config, JavaType valueType, 290 KeyDeserializer kd) { 291 return new KeyDeserializer() { 292 @Override 293 public Object deserializeKey(String key, 294 DeserializationContext ctxt) throws IOException, 295 JsonProcessingException { 296 return "foo"; 297 } 298 }; 299 } 300 } 301 302 static class UCStringDeserializer extends StdScalarDeserializer<String> 303 { 304 private final JsonDeserializer<?> _deser; 305 306 public UCStringDeserializer(JsonDeserializer<?> orig) { 307 super(String.class); 308 _deser = orig; 309 } 310 311 @Override 312 public String deserialize(JsonParser p, DeserializationContext ctxt) 313 throws IOException { 314 Object ob = _deser.deserialize(p, ctxt); 315 return String.valueOf(ob).toUpperCase(); 316 } 317 } 318 319 /* 320 /******************************************************** 321 /* Test methods 322 /******************************************************** 323 */ 324 325 private final ObjectMapper MAPPER = newJsonMapper(); 326 327 /** 328 * Test to verify details of how trying to deserialize into 329 * abstract type should fail (if there is no way to determine 330 * actual type information for the concrete type to use) 331 */ 332 public void testAbstractFailure() throws Exception 333 { 334 try { 335 MAPPER.readValue("{ \"x\" : 3 }", Abstract.class); 336 fail("Should fail on trying to deserialize abstract type"); 337 } catch (JsonProcessingException e) { 338 verifyException(e, "cannot construct"); 339 } 340 } 341 public void testPropertyRemoval() throws Exception 342 { 343 ObjectMapper mapper = new ObjectMapper(); 344 mapper.registerModule(new ModuleImpl(new RemovingModifier("a"))); 345 Bean bean = mapper.readValue("{\"b\":\"2\"}", Bean.class); 346 assertEquals("2", bean.b); 347 // and 'a' has its default value: 348 assertEquals("a", bean.a); 349 } 350 351 public void testDeserializerReplacement() throws Exception 352 { 353 ObjectMapper mapper = new ObjectMapper(); 354 mapper.registerModule(new ModuleImpl(new ReplacingModifier(new BogusBeanDeserializer("foo", "bar")))); 355 Bean bean = mapper.readValue("{\"a\":\"xyz\"}", Bean.class); 356 // custom deserializer always produces instance like this: 357 assertEquals("foo", bean.a); 358 assertEquals("bar", bean.b); 359 } 360 361 public void testIssue476() throws Exception 362 { 363 final String JSON = "{\"value1\" : {\"name\" : \"fruit\", \"value\" : \"apple\"}, \"value2\" : {\"name\" : \"color\", \"value\" : \"red\"}}"; 364 365 ObjectMapper mapper = new ObjectMapper(); 366 mapper.registerModule(new Issue476Module()); 367 mapper.readValue(JSON, Issue476Bean.class); 368 369 // there are 2 properties 370 assertEquals(2, Issue476Deserializer.propCount); 371 } 372 373 // [databind#120] 374 public void testModifyArrayDeserializer() throws Exception 375 { 376 ObjectMapper mapper = new ObjectMapper(); 377 mapper.registerModule(new SimpleModule("test") 378 .setDeserializerModifier(new ArrayDeserializerModifier())); 379 Object[] result = mapper.readValue("[1,2]", Object[].class); 380 assertEquals(1, result.length); 381 assertEquals("foo", result[0]); 382 } 383 384 public void testModifyCollectionDeserializer() throws Exception 385 { 386 ObjectMapper mapper = new ObjectMapper(); 387 mapper.registerModule(new SimpleModule("test") 388 .setDeserializerModifier(new CollectionDeserializerModifier()) 389 ); 390 List<?> result = mapper.readValue("[1,2]", List.class); 391 assertEquals(1, result.size()); 392 assertEquals("foo", result.get(0)); 393 } 394 395 public void testModifyMapDeserializer() throws Exception 396 { 397 ObjectMapper mapper = new ObjectMapper(); 398 mapper.registerModule(new SimpleModule("test") 399 .setDeserializerModifier(new MapDeserializerModifier()) 400 ); 401 Map<?,?> result = mapper.readValue("{\"a\":1,\"b\":2}", Map.class); 402 assertEquals(1, result.size()); 403 assertEquals("foo", result.get("a")); 404 } 405 406 public void testModifyEnumDeserializer() throws Exception 407 { 408 ObjectMapper mapper = new ObjectMapper(); 409 mapper.registerModule(new SimpleModule("test") 410 .setDeserializerModifier(new EnumDeserializerModifier()) 411 ); 412 Object result = mapper.readValue(quote("B"), EnumABC.class); 413 assertEquals("foo", result); 414 } 415 416 public void testModifyKeyDeserializer() throws Exception 417 { 418 ObjectMapper mapper = new ObjectMapper(); 419 mapper.registerModule(new SimpleModule("test") 420 .setDeserializerModifier(new KeyDeserializerModifier()) 421 ); 422 Map<?,?> result = mapper.readValue("{\"a\":1}", Map.class); 423 assertEquals(1, result.size()); 424 assertEquals("foo", result.entrySet().iterator().next().getKey()); 425 } 426 427 /** 428 * Test to verify that even standard deserializers will result in `modifyDeserializer` 429 * getting appropriately called. 430 */ 431 public void testModifyStdScalarDeserializer() throws Exception 432 { 433 ObjectMapper mapper = new ObjectMapper(); 434 mapper.registerModule(new SimpleModule("test") 435 .setDeserializerModifier(new BeanDeserializerModifier() { 436 @Override 437 public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, 438 BeanDescription beanDesc, JsonDeserializer<?> deser) { 439 if (beanDesc.getBeanClass() == String.class) { 440 return new UCStringDeserializer(deser); 441 } 442 return deser; 443 } 444 })); 445 Object result = mapper.readValue(quote("abcDEF"), String.class); 446 assertEquals("ABCDEF", result); 447 } 448 449 public void testAddOrReplacePropertyIsUsedOnDeserialization() throws Exception { 450 ObjectMapper mapper = new ObjectMapper(); 451 mapper.registerModule(new Issue1912Module()); 452 453 Issue1912Bean result = mapper.readValue("{\"subBean\": {\"a\":\"foo\"}}", Issue1912Bean.class); 454 assertEquals("foo_custom", result.subBean.a); 455 } 456 } 457