blob: dacf4231ec3b82075339c85d277987a83ce245cc [file] [log] [blame]
Tatu Salorantaea665992017-02-02 21:54:45 -08001package com.fasterxml.jackson.databind.deser.filter;
Tatu Salorantaa63c2032011-12-24 00:26:53 -08002
3import java.io.*;
Cowtowncoder3bd5de62015-10-28 13:36:23 -07004import java.util.*;
Tatu Salorantaa63c2032011-12-24 00:26:53 -08005
6import com.fasterxml.jackson.annotation.*;
7import com.fasterxml.jackson.core.*;
Cowtowncoder3bd5de62015-10-28 13:36:23 -07008import com.fasterxml.jackson.core.type.TypeReference;
Tatu Salorantaa63c2032011-12-24 00:26:53 -08009import com.fasterxml.jackson.databind.*;
Tatu Saloranta15212e82012-02-20 22:13:25 -080010import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
Tatu Salorantaa63c2032011-12-24 00:26:53 -080011
12/**
13 * Unit tests for checking handling of unknown properties
14 */
Tatu Saloranta15212e82012-02-20 22:13:25 -080015public class TestUnknownPropertyDeserialization
Tatu Salorantaa63c2032011-12-24 00:26:53 -080016 extends BaseMapTest
17{
18 final static String JSON_UNKNOWN_FIELD = "{ \"a\" : 1, \"foo\" : [ 1, 2, 3], \"b\" : -1 }";
19
20 /*
21 /**********************************************************
22 /* Helper classes
23 /**********************************************************
24 */
25
26 final static class TestBean
27 {
28 String _unknown;
29
30 int _a, _b;
31
32 public TestBean() { }
33
34 public void setA(int a) { _a = a; }
35 public void setB(int b) { _b = b; }
36
37 public void markUnknown(String unk) { _unknown = unk; }
38 }
39
40 /**
41 * Simple {@link DeserializationProblemHandler} sub-class that
42 * just marks unknown property/ies when encountered, along with
43 * Json value of the property.
44 */
Tatu Salorantaf2c40cb2016-05-10 20:21:32 -070045 static class MyHandler
Tatu Salorantaa63c2032011-12-24 00:26:53 -080046 extends DeserializationProblemHandler
47 {
48 @Override
Tatu Saloranta060ce112012-02-01 22:18:09 -080049 public boolean handleUnknownProperty(DeserializationContext ctxt,
50 JsonParser jp, JsonDeserializer<?> deserializer,
51 Object bean, String propertyName)
Tatu Salorantaa63c2032011-12-24 00:26:53 -080052 throws IOException, JsonProcessingException
53 {
Tatu Salorantaa63c2032011-12-24 00:26:53 -080054 // very simple, just to verify that we do see correct token type
55 ((TestBean) bean).markUnknown(propertyName+":"+jp.getCurrentToken().toString());
56 // Yup, we are good to go; must skip whatever value we'd have:
57 jp.skipChildren();
58 return true;
59 }
60 }
61
62 @JsonIgnoreProperties({"b", "c"})
63 static class IgnoreSome
64 {
65 public int a, b;
66 private String c, d;
67
68 public IgnoreSome() { }
69
70 public String c() { return c; }
71 public void setC(String value) { c = value; }
72 public String d() { return d; }
73 public void setD(String value) { d = value; }
74 }
75
76 @JsonIgnoreProperties(ignoreUnknown=true)
77 static class IgnoreUnknown {
78 public int a;
79 }
80
81 @SuppressWarnings("serial")
82 @JsonIgnoreProperties({"a", "d"})
83 static class IgnoreMap extends HashMap<String,Object> { }
84
85 static class ImplicitIgnores {
86 @JsonIgnore public int a;
87 @JsonIgnore public void setB(int b) { }
88 public int c;
89 }
90
Tatu Saloranta15212e82012-02-20 22:13:25 -080091 // // Ignored as per [JACKSON-787]
92
93 static class XYZWrapper1 {
94 @JsonIgnoreProperties({"x"})
95 public YZ value;
96 }
97
98 static class YZ {
99 public int y, z;
100 }
101
102 static class XYZWrapper2 {
103 @JsonIgnoreProperties({"y"})
104 public X value;
105 }
106
107 @JsonIgnoreProperties({"z"})
108 static class X {
109 public int x;
110 }
111
112 static class MapWithoutX {
113 @JsonIgnoreProperties("x")
114 public Map<String,Integer> values;
115 }
Tatu1ae2d622014-05-15 14:13:09 -0700116
Cowtowncoder3bd5de62015-10-28 13:36:23 -0700117 // [databind#987]
118 static class Bean987 {
119 public String aProperty;
120 }
121
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800122 /*
123 /**********************************************************
124 /* Test methods
125 /**********************************************************
126 */
127
Tatu Salorantab5efcf72017-04-19 10:49:59 -0700128 private final ObjectMapper MAPPER = newObjectMapper();
Cowtowncoder3bd5de62015-10-28 13:36:23 -0700129
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800130 /**
131 * By default we should just get an exception if an unknown property
132 * is encountered
133 */
Cowtowncoder3bd5de62015-10-28 13:36:23 -0700134 public void testUnknownHandlingDefault() throws Exception
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800135 {
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800136 try {
Tatu Saloranta15212e82012-02-20 22:13:25 -0800137 MAPPER.readValue(new StringReader(JSON_UNKNOWN_FIELD), TestBean.class);
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800138 } catch (JsonMappingException jex) {
139 verifyException(jex, "Unrecognized field \"foo\"");
140 }
141 }
142
143 /**
144 * Test that verifies that it is possible to ignore unknown properties using
145 * {@link DeserializationProblemHandler}.
146 */
Cowtowncoder3bd5de62015-10-28 13:36:23 -0700147 public void testUnknownHandlingIgnoreWithHandler() throws Exception
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800148 {
Tatu Salorantab5efcf72017-04-19 10:49:59 -0700149 ObjectMapper mapper = newObjectMapper();
Tatudfed9242012-01-19 12:31:44 -0800150 mapper.clearProblemHandlers();
151 mapper.addHandler(new MyHandler());
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800152 TestBean result = mapper.readValue(new StringReader(JSON_UNKNOWN_FIELD), TestBean.class);
153 assertNotNull(result);
154 assertEquals(1, result._a);
155 assertEquals(-1, result._b);
156 assertEquals("foo:START_ARRAY", result._unknown);
157 }
158
159 /**
shaiyallin07bc9b72012-07-01 17:32:00 +0300160 * Test that verifies that it is possible to ignore unknown properties using
161 * {@link DeserializationProblemHandler} and an ObjectReader.
162 */
Cowtowncoder3bd5de62015-10-28 13:36:23 -0700163 public void testUnknownHandlingIgnoreWithHandlerAndObjectReader() throws Exception
shaiyallin07bc9b72012-07-01 17:32:00 +0300164 {
Tatu Salorantab5efcf72017-04-19 10:49:59 -0700165 ObjectMapper mapper = newObjectMapper();
shaiyallin07bc9b72012-07-01 17:32:00 +0300166 mapper.clearProblemHandlers();
Cowtowncodera936f432015-05-12 12:33:46 -0700167 TestBean result = mapper.readerFor(TestBean.class).withHandler(new MyHandler()).readValue(new StringReader(JSON_UNKNOWN_FIELD));
shaiyallin07bc9b72012-07-01 17:32:00 +0300168 assertNotNull(result);
169 assertEquals(1, result._a);
170 assertEquals(-1, result._b);
171 assertEquals("foo:START_ARRAY", result._unknown);
172 }
173
174 /**
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800175 * Test for checking that it is also possible to simply suppress
176 * error reporting for unknown properties.
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800177 */
Cowtowncoder3bd5de62015-10-28 13:36:23 -0700178 public void testUnknownHandlingIgnoreWithFeature() throws Exception
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800179 {
Tatu Salorantab5efcf72017-04-19 10:49:59 -0700180 ObjectMapper mapper = newObjectMapper();
Tatu9610aff2012-02-02 11:30:08 -0800181 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800182 TestBean result = null;
183 try {
184 result = mapper.readValue(new StringReader(JSON_UNKNOWN_FIELD), TestBean.class);
185 } catch (JsonMappingException jex) {
186 fail("Did not expect a problem, got: "+jex.getMessage());
187 }
188 assertNotNull(result);
189 assertEquals(1, result._a);
190 assertNull(result._unknown);
191 assertEquals(-1, result._b);
192 }
193
Cowtowncoder3bd5de62015-10-28 13:36:23 -0700194 public void testWithClassIgnore() throws Exception
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800195 {
Tatu Saloranta15212e82012-02-20 22:13:25 -0800196 IgnoreSome result = MAPPER.readValue("{ \"a\":1,\"b\":2,\"c\":\"x\",\"d\":\"y\"}",
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800197 IgnoreSome.class);
198 // first: should deserialize 2 of properties normally
199 assertEquals(1, result.a);
200 assertEquals("y", result.d());
201 // and not take other 2
202 assertEquals(0, result.b);
203 assertNull(result.c());
204 }
205
206 /// @since 1.4
207 public void testClassIgnoreWithMap() throws Exception
208 {
209 // Let's actually use incompatible types for "a" and "d"; should not matter when ignored
Tatu Saloranta15212e82012-02-20 22:13:25 -0800210 IgnoreMap result = MAPPER.readValue
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800211 ("{ \"a\":[ 1],\n"
212 +"\"b\":2,\n"
213 +"\"c\": \"x\",\n"
214 +"\"d\":false }", IgnoreMap.class);
215 assertEquals(2, result.size());
216 Object ob = result.get("b");
217 assertEquals(Integer.class, ob.getClass());
218 assertEquals(Integer.valueOf(2), ob);
219 assertEquals("x", result.get("c"));
220 assertFalse(result.containsKey("a"));
221 assertFalse(result.containsKey("d"));
222 }
223
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800224 public void testClassWithIgnoreUnknown() throws Exception
225 {
Tatu Saloranta15212e82012-02-20 22:13:25 -0800226 IgnoreUnknown result = MAPPER.readValue
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800227 ("{\"b\":3,\"c\":[1,2],\"x\":{ },\"a\":-3}", IgnoreUnknown.class);
228 assertEquals(-3, result.a);
229 }
230
231 /**
232 * Test that verifies that use of {@link JsonIgnore} will add implicit
233 * skipping of matching properties.
234 */
235 public void testClassWithUnknownAndIgnore() throws Exception
236 {
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800237 // should be ok: "a" and "b" ignored, "c" mapped:
Tatu Saloranta15212e82012-02-20 22:13:25 -0800238 ImplicitIgnores result = MAPPER.readValue
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800239 ("{\"a\":1,\"b\":2,\"c\":3 }", ImplicitIgnores.class);
240 assertEquals(3, result.c);
241
242 // but "d" is not defined, so should still error
243 try {
Tatu Saloranta15212e82012-02-20 22:13:25 -0800244 MAPPER.readValue("{\"a\":1,\"b\":2,\"c\":3,\"d\":4 }", ImplicitIgnores.class);
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800245 } catch (JsonMappingException e) {
246 verifyException(e, "Unrecognized field \"d\"");
247 }
248 }
Tatu Saloranta15212e82012-02-20 22:13:25 -0800249
250 public void testPropertyIgnoral() throws Exception
251 {
252 XYZWrapper1 result = MAPPER.readValue("{\"value\":{\"y\":2,\"x\":1,\"z\":3}}", XYZWrapper1.class);
253 assertEquals(2, result.value.y);
254 assertEquals(3, result.value.z);
255 }
256
257 public void testPropertyIgnoralWithClass() throws Exception
258 {
Tatu Saloranta24407a42016-05-01 14:07:51 -0700259 XYZWrapper2 result = MAPPER.readValue("{\"value\":{\"y\":2,\"x\":1,\"z\":3}}",
260 XYZWrapper2.class);
Tatu Saloranta15212e82012-02-20 22:13:25 -0800261 assertEquals(1, result.value.x);
262 }
263
264 public void testPropertyIgnoralForMap() throws Exception
265 {
266 MapWithoutX result = MAPPER.readValue("{\"values\":{\"x\":1,\"y\":2}}", MapWithoutX.class);
267 assertNotNull(result.values);
268 assertEquals(1, result.values.size());
269 assertEquals(Integer.valueOf(2), result.values.get("y"));
270 }
Cowtowncoder3bd5de62015-10-28 13:36:23 -0700271
272 public void testIssue987() throws Exception
273 {
Tatu Salorantab5efcf72017-04-19 10:49:59 -0700274 ObjectMapper jsonMapper = newObjectMapper();
Cowtowncoder3bd5de62015-10-28 13:36:23 -0700275 jsonMapper.addHandler(new DeserializationProblemHandler() {
276 @Override
277 public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser p, JsonDeserializer<?> deserializer, Object beanOrClass, String propertyName) throws IOException, JsonProcessingException {
278 p.skipChildren();
279 return true;
280 }
281 });
282
283 String input = "[{\"aProperty\":\"x\",\"unknown\":{\"unknown\":{}}}]";
284 List<Bean987> deserializedList = jsonMapper.readValue(input,
285 new TypeReference<List<Bean987>>() { });
286 assertEquals(1, deserializedList.size());
287 }
Tatu Salorantaa63c2032011-12-24 00:26:53 -0800288}