• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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