• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.deser;
2 
3 import java.util.*;
4 
5 import com.fasterxml.jackson.annotation.*;
6 
7 import com.fasterxml.jackson.databind.*;
8 
9 /**
10  * Unit tests for verifying that {@link JsonAnySetter} annotation
11  * works as expected.
12  */
13 public class AnySetterTest
14     extends BaseMapTest
15 {
16     static class MapImitator
17     {
18         HashMap<String,Object> _map;
19 
MapImitator()20         public MapImitator() {
21             _map = new HashMap<String,Object>();
22         }
23 
24         @JsonAnySetter
addEntry(String key, Object value)25         void addEntry(String key, Object value)
26         {
27             _map.put(key, value);
28         }
29     }
30 
31     // for [databind#1376]
32     static class MapImitatorDisabled extends MapImitator
33     {
34         @Override
35         @JsonAnySetter(enabled=false)
addEntry(String key, Object value)36         void addEntry(String key, Object value) {
37             throw new RuntimeException("Should not get called");
38         }
39     }
40 
41     /**
42      * Let's also verify that it is possible to define different
43      * value: not often useful, but possible.
44      */
45     static class MapImitatorWithValue
46     {
47         HashMap<String,int[]> _map;
48 
MapImitatorWithValue()49         public MapImitatorWithValue() {
50             _map = new HashMap<String,int[]>();
51         }
52 
53         @JsonAnySetter
addEntry(String key, int[] value)54         void addEntry(String key, int[] value)
55         {
56             _map.put(key, value);
57         }
58     }
59 
60     // Bad; 2 "any setters"
61     static class Broken
62     {
63         @JsonAnySetter
addEntry1(String key, Object value)64         void addEntry1(String key, Object value) { }
65         @JsonAnySetter
addEntry2(String key, Object value)66         void addEntry2(String key, Object value) { }
67     }
68 
69     @JsonIgnoreProperties("dummy")
70     static class Ignored
71     {
72         HashMap<String,Object> map = new HashMap<String,Object>();
73 
74         @JsonIgnore
75         public String bogus;
76 
77         @JsonAnySetter
addEntry(String key, Object value)78         void addEntry(String key, Object value)
79         {
80             map.put(key, value);
81         }
82     }
83 
84     static class Bean744
85     {
86         protected Map<String,Object> additionalProperties;
87 
88         @JsonAnySetter
addAdditionalProperty(String key, Object value)89         public void addAdditionalProperty(String key, Object value) {
90             if (additionalProperties == null) additionalProperties = new HashMap<String, Object>();
91             additionalProperties.put(key,value);
92         }
93 
setAdditionalProperties(Map<String, Object> additionalProperties)94         public void setAdditionalProperties(Map<String, Object> additionalProperties) {
95             this.additionalProperties = additionalProperties;
96         }
97 
98         @JsonAnyGetter
getAdditionalProperties()99         public Map<String,Object> getAdditionalProperties() { return additionalProperties; }
100 
101         @JsonIgnore
getName()102         public String getName() {
103            return (String) additionalProperties.get("name");
104         }
105     }
106 
107     static class Bean797Base
108     {
109         @JsonAnyGetter
getUndefinedProperties()110         public Map<String, JsonNode> getUndefinedProperties() {
111             throw new IllegalStateException("Should not call parent version!");
112         }
113     }
114 
115     static class Bean797BaseImpl extends Bean797Base
116     {
117         @Override
getUndefinedProperties()118         public Map<String, JsonNode> getUndefinedProperties() {
119             return new HashMap<String, JsonNode>();
120         }
121     }
122 
123     @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
124     static abstract class Base { }
125 
126     static class Impl extends Base {
127         public String value;
128 
Impl()129         public Impl() { }
Impl(String v)130         public Impl(String v) { value = v; }
131     }
132 
133     static class PolyAnyBean
134     {
135         protected Map<String,Base> props = new HashMap<String,Base>();
136 
137         @JsonAnyGetter
props()138         public Map<String,Base> props() {
139             return props;
140         }
141 
142         @JsonAnySetter
prop(String name, Base value)143         public void prop(String name, Base value) {
144             props.put(name, value);
145         }
146     }
147 
148     static class JsonAnySetterOnMap {
149         public int id;
150 
151         @JsonAnySetter
152         protected HashMap<String, String> other = new HashMap<String, String>();
153 
154         @JsonAnyGetter
any()155         public Map<String, String> any() {
156             return other;
157         }
158     }
159 
160     static class JsonAnySetterOnNullMap {
161         public int id;
162 
163         @JsonAnySetter
164         protected HashMap<String, String> other;
165 
166         @JsonAnyGetter
any()167         public Map<String, String> any() {
168             return other;
169         }
170     }
171 
172     static class MyGeneric<T>
173     {
174         private String staticallyMappedProperty;
175         private Map<T, Integer> dynamicallyMappedProperties = new HashMap<T, Integer>();
176 
getStaticallyMappedProperty()177         public String getStaticallyMappedProperty() {
178             return staticallyMappedProperty;
179         }
180 
181         @JsonAnySetter
addDynamicallyMappedProperty(T key, int value)182         public void addDynamicallyMappedProperty(T key, int value) {
183             dynamicallyMappedProperties.put(key, value);
184         }
185 
setStaticallyMappedProperty(String staticallyMappedProperty)186         public void setStaticallyMappedProperty(String staticallyMappedProperty) {
187             this.staticallyMappedProperty = staticallyMappedProperty;
188         }
189 
190         @JsonAnyGetter
getDynamicallyMappedProperties()191         public Map<T, Integer> getDynamicallyMappedProperties() {
192             return dynamicallyMappedProperties;
193         }
194     }
195 
196     static class MyWrapper
197     {
198         private MyGeneric<String> myStringGeneric;
199         private MyGeneric<Integer> myIntegerGeneric;
200 
getMyStringGeneric()201         public MyGeneric<String> getMyStringGeneric() {
202             return myStringGeneric;
203         }
204 
setMyStringGeneric(MyGeneric<String> myStringGeneric)205         public void setMyStringGeneric(MyGeneric<String> myStringGeneric) {
206             this.myStringGeneric = myStringGeneric;
207         }
208 
getMyIntegerGeneric()209         public MyGeneric<Integer> getMyIntegerGeneric() {
210             return myIntegerGeneric;
211         }
212 
setMyIntegerGeneric(MyGeneric<Integer> myIntegerGeneric)213         public void setMyIntegerGeneric(MyGeneric<Integer> myIntegerGeneric) {
214             this.myIntegerGeneric = myIntegerGeneric;
215         }
216     }
217 
218     // [databind#349]
219     static class Bean349
220     {
221         public String type;
222         public int x, y;
223 
224         private Map<String, Object> props = new HashMap<>();
225 
226         @JsonAnySetter
addProperty(String key, Object value)227         public void addProperty(String key, Object value) {
228             props.put(key, value);
229         }
230 
231         @JsonAnyGetter
getProperties()232         public Map<String, Object> getProperties() {
233             return props;
234         }
235 
236         @JsonUnwrapped
237         public IdentityDTO349 identity;
238     }
239 
240     static class IdentityDTO349 {
241         public int x, y;
242     }
243 
244     /*
245     /**********************************************************
246     /* Test methods
247     /**********************************************************
248      */
249 
250     private final ObjectMapper MAPPER = new ObjectMapper();
251 
testSimpleMapImitation()252     public void testSimpleMapImitation() throws Exception
253     {
254         MapImitator mapHolder = MAPPER.readValue
255             ("{ \"a\" : 3, \"b\" : true, \"c\":[1,2,3] }", MapImitator.class);
256         Map<String,Object> result = mapHolder._map;
257         assertEquals(3, result.size());
258         assertEquals(Integer.valueOf(3), result.get("a"));
259         assertEquals(Boolean.TRUE, result.get("b"));
260         Object ob = result.get("c");
261         assertTrue(ob instanceof List<?>);
262         List<?> l = (List<?>)ob;
263         assertEquals(3, l.size());
264         assertEquals(Integer.valueOf(3), l.get(2));
265     }
266 
testAnySetterDisable()267     public void testAnySetterDisable() throws Exception
268     {
269         try {
270             MAPPER.readValue(aposToQuotes("{'value':3}"),
271                     MapImitatorDisabled.class);
272             fail("Should not pass");
273         } catch (JsonMappingException e) {
274             verifyException(e, "Unrecognized field \"value\"");
275         }
276 
277     }
278 
testSimpleTyped()279     public void testSimpleTyped() throws Exception
280     {
281         MapImitatorWithValue mapHolder = MAPPER.readValue
282             ("{ \"a\" : [ 3, -1 ], \"b\" : [ ] }", MapImitatorWithValue.class);
283         Map<String,int[]> result = mapHolder._map;
284         assertEquals(2, result.size());
285         assertEquals(new int[] { 3, -1 }, result.get("a"));
286         assertEquals(new int[0], result.get("b"));
287     }
288 
testBrokenWithDoubleAnnotations()289     public void testBrokenWithDoubleAnnotations() throws Exception
290     {
291         try {
292             @SuppressWarnings("unused")
293             Broken b = MAPPER.readValue("{ \"a\" : 3 }", Broken.class);
294             fail("Should have gotten an exception");
295         } catch (JsonMappingException e) {
296             verifyException(e, "Multiple 'any-setter' methods");
297         }
298     }
299 
testIgnored()300     public void testIgnored() throws Exception
301     {
302         ObjectMapper mapper = new ObjectMapper();
303         mapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
304         _testIgnorals(mapper);
305     }
306 
testIgnoredPart2()307     public void testIgnoredPart2() throws Exception
308     {
309         ObjectMapper mapper = new ObjectMapper();
310         mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
311         _testIgnorals(mapper);
312     }
313 
testProblem744()314     public void testProblem744() throws Exception
315     {
316         Bean744 bean = MAPPER.readValue("{\"name\":\"Bob\"}", Bean744.class);
317         assertNotNull(bean.additionalProperties);
318         assertEquals(1, bean.additionalProperties.size());
319         assertEquals("Bob", bean.additionalProperties.get("name"));
320     }
321 
testIssue797()322     public void testIssue797() throws Exception
323     {
324         String json = MAPPER.writeValueAsString(new Bean797BaseImpl());
325         assertEquals("{}", json);
326     }
327 
328     // [Issue#337]
testPolymorphic()329     public void testPolymorphic() throws Exception
330     {
331         PolyAnyBean input = new PolyAnyBean();
332         input.props.put("a", new Impl("xyz"));
333 
334         String json = MAPPER.writeValueAsString(input);
335 
336 //        System.err.println("JSON: "+json);
337 
338         PolyAnyBean result = MAPPER.readValue(json, PolyAnyBean.class);
339         assertEquals(1, result.props.size());
340         Base ob = result.props.get("a");
341         assertNotNull(ob);
342         assertTrue(ob instanceof Impl);
343         assertEquals("xyz", ((Impl) ob).value);
344     }
345 
testJsonAnySetterOnMap()346 	public void testJsonAnySetterOnMap() throws Exception {
347 		JsonAnySetterOnMap result = MAPPER.readValue("{\"id\":2,\"name\":\"Joe\", \"city\":\"New Jersey\"}",
348 		        JsonAnySetterOnMap.class);
349 		assertEquals(2, result.id);
350 		assertEquals("Joe", result.other.get("name"));
351 		assertEquals("New Jersey", result.other.get("city"));
352 	}
353 
testJsonAnySetterOnNullMap()354 	public void testJsonAnySetterOnNullMap() throws Exception {
355 		JsonAnySetterOnNullMap result = MAPPER.readValue("{\"id\":2,\"name\":\"Joe\", \"city\":\"New Jersey\"}",
356 		        JsonAnySetterOnNullMap.class);
357 		assertEquals(2, result.id);
358 		assertNull(result.other);
359     }
360 
361     final static String UNWRAPPED_JSON_349 = aposToQuotes(
362             "{ 'type' : 'IST',\n"
363                     +" 'x' : 3,\n"
364                     //+" 'name' : 'BLAH-New',\n"
365                     //+" 'description' : 'namespace.name: X THIN FIR.DR-WD12-New',\n"
366                     +" 'ZoomLinks': [ 'foofoofoofoo', 'barbarbarbar' ],\n"
367                     +" 'y' : 4, 'z' : 8 }"
368             );
369 
370     // [databind#349]
testUnwrappedWithAny()371     public void testUnwrappedWithAny() throws Exception
372     {
373         final ObjectMapper mapper = objectMapper();
374         Bean349 value = mapper.readValue(UNWRAPPED_JSON_349,  Bean349.class);
375         assertNotNull(value);
376         assertEquals(3, value.x);
377         assertEquals(4, value.y);
378         assertEquals(2, value.props.size());
379     }
380 
381     // [databind#349]
testUnwrappedWithAnyAsUpdate()382     public void testUnwrappedWithAnyAsUpdate() throws Exception
383     {
384         final ObjectMapper mapper = objectMapper();
385         Bean349 bean = mapper.readerFor(Bean349.class)
386                 .withValueToUpdate(new Bean349())
387                 .readValue(UNWRAPPED_JSON_349);
388         assertEquals(3, bean.x);
389         assertEquals(4, bean.y);
390         assertEquals(2, bean.props.size());
391     }
392 
393     // [databind#1035]
testGenericAnySetter()394     public void testGenericAnySetter() throws Exception
395     {
396         ObjectMapper mapper = new ObjectMapper();
397 
398         Map<String, Integer> stringGenericMap = new HashMap<String, Integer>();
399         stringGenericMap.put("testStringKey", 5);
400         Map<Integer, Integer> integerGenericMap = new HashMap<Integer, Integer>();
401         integerGenericMap.put(111, 6);
402 
403         MyWrapper deserialized = mapper.readValue(aposToQuotes(
404                 "{'myStringGeneric':{'staticallyMappedProperty':'Test','testStringKey':5},'myIntegerGeneric':{'staticallyMappedProperty':'Test2','111':6}}"
405                 ), MyWrapper.class);
406         MyGeneric<String> stringGeneric = deserialized.getMyStringGeneric();
407         MyGeneric<Integer> integerGeneric = deserialized.getMyIntegerGeneric();
408 
409         assertNotNull(stringGeneric);
410         assertEquals(stringGeneric.getStaticallyMappedProperty(), "Test");
411         for(Map.Entry<String, Integer> entry : stringGeneric.getDynamicallyMappedProperties().entrySet()) {
412             assertTrue("A key in MyGeneric<String> is not an String.", entry.getKey() instanceof String);
413             assertTrue("A value in MyGeneric<Integer> is not an Integer.", entry.getValue() instanceof Integer);
414         }
415         assertEquals(stringGeneric.getDynamicallyMappedProperties(), stringGenericMap);
416 
417         assertNotNull(integerGeneric);
418         assertEquals(integerGeneric.getStaticallyMappedProperty(), "Test2");
419         for(Map.Entry<Integer, Integer> entry : integerGeneric.getDynamicallyMappedProperties().entrySet()) {
420             Object key = entry.getKey();
421             assertEquals("A key in MyGeneric<Integer> is not an Integer.", Integer.class, key.getClass());
422             Object value = entry.getValue();
423             assertEquals("A value in MyGeneric<Integer> is not an Integer.", Integer.class, value.getClass());
424         }
425         assertEquals(integerGeneric.getDynamicallyMappedProperties(), integerGenericMap);
426     }
427 
428     /*
429     /**********************************************************
430     /* Private helper methods
431     /**********************************************************
432      */
433 
_testIgnorals(ObjectMapper mapper)434     private void _testIgnorals(ObjectMapper mapper) throws Exception
435     {
436         Ignored bean = mapper.readValue("{\"name\":\"Bob\", \"bogus\": [ 1, 2, 3], \"dummy\" : 13 }", Ignored.class);
437         // as of 2.0, @JsonIgnoreProperties does block; @JsonIgnore not
438         assertNull(bean.map.get("dummy"));
439         assertEquals("[1, 2, 3]", ""+bean.map.get("bogus"));
440         assertEquals("Bob", bean.map.get("name"));
441         assertEquals(2, bean.map.size());
442     }
443 }
444