• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 package com.fasterxml.jackson.databind.deser.creators;
3 
4 import java.io.IOException;
5 import java.util.List;
6 import java.util.Map;
7 
8 import com.fasterxml.jackson.annotation.*;
9 import com.fasterxml.jackson.core.*;
10 import com.fasterxml.jackson.databind.*;
11 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
12 import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
13 import com.fasterxml.jackson.databind.exc.MismatchedInputException;
14 import com.fasterxml.jackson.databind.exc.ValueInstantiationException;
15 
16 public class TestCreators2 extends BaseMapTest
17 {
18     static class HashTest
19     {
20         final byte[] bytes;
21         final String type;
22 
23         @JsonCreator
HashTest(@sonProperty"bytes") @sonDeserializeusing = BytesDeserializer.class) final byte[] bytes, @JsonProperty("type") final String type)24         public HashTest(@JsonProperty("bytes") @JsonDeserialize(using = BytesDeserializer.class) final byte[] bytes,
25                 @JsonProperty("type") final String type)
26         {
27             this.bytes = bytes;
28             this.type = type;
29         }
30     }
31 
32     static class BytesDeserializer extends JsonDeserializer<byte[]>
33     {
34         @Override
deserialize(JsonParser jp, DeserializationContext ctxt)35         public byte[] deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
36             String str = jp.getText();
37             return str.getBytes("UTF-8");
38         }
39     }
40 
41     static class Primitives
42     {
43         protected int x = 3;
44         protected double d = -0.5;
45         protected boolean b = true;
46 
47         @JsonCreator
Primitives(@sonProperty"x") int x, @JsonProperty("d") double d, @JsonProperty("b") boolean b)48         public Primitives(@JsonProperty("x") int x,
49                 @JsonProperty("d") double d,
50                 @JsonProperty("b") boolean b)
51         {
52             this.x = x;
53             this.d = d;
54             this.b = b;
55         }
56     }
57 
58     protected static class Test431Container {
59         protected final List<Item431> items;
60 
61         @JsonCreator
Test431Container(@sonProperty"items") final List<Item431> i)62         public Test431Container(@JsonProperty("items") final List<Item431> i) {
63             items = i;
64         }
65     }
66 
67     @JsonIgnoreProperties(ignoreUnknown = true)
68     protected static class Item431 {
69         protected final String id;
70 
71         @JsonCreator
Item431(@sonProperty"id") String id)72         public Item431(@JsonProperty("id") String id) {
73             this.id = id;
74         }
75     }
76 
77     // Test class for verifying that creator-call failures are reported as checked exceptions
78     static class BeanFor438 {
79         @JsonCreator
BeanFor438(@sonProperty"name") String s)80         public BeanFor438(@JsonProperty("name") String s) {
81             throw new IllegalArgumentException("I don't like that name!");
82         }
83     }
84 
85     // For [JACKSON-470]: should be appropriately detected, reported error about
86     static class BrokenCreatorBean
87     {
88         protected String bar;
89 
90         @JsonCreator
BrokenCreatorBean(@sonProperty"bar") String bar1, @JsonProperty("bar") String bar2)91         public BrokenCreatorBean(@JsonProperty("bar") String bar1, @JsonProperty("bar") String bar2) {
92             bar = ""+bar1+"/"+bar2;
93         }
94     }
95 
96     // For [JACKSON-541]: should not need @JsonCreator if SerializationFeature.AUTO_DETECT_CREATORS is on.
97     static class AutoDetectConstructorBean
98     {
99         protected final String foo;
100         protected final String bar;
101 
AutoDetectConstructorBean(@sonProperty"bar") String bar, @JsonProperty("foo") String foo)102         public AutoDetectConstructorBean(@JsonProperty("bar") String bar,
103                 @JsonProperty("foo") String foo){
104             this.bar = bar;
105             this.foo = foo;
106         }
107     }
108 
109     static class BustedCtor {
110         @JsonCreator
BustedCtor(@sonProperty"a") String value)111         BustedCtor(@JsonProperty("a") String value) {
112             throw new IllegalArgumentException("foobar");
113         }
114     }
115 
116     static class IgnoredCtor
117     {
118         @JsonIgnore
IgnoredCtor(String arg)119         public IgnoredCtor(String arg) {
120             throw new RuntimeException("Should never use this constructor");
121         }
122 
IgnoredCtor()123         public IgnoredCtor() { }
124     }
125 
126     abstract static class AbstractBase {
127         @JsonCreator
create(Map<String,Object> props)128         public static AbstractBase create(Map<String,Object> props)
129         {
130             return new AbstractBaseImpl(props);
131         }
132     }
133 
134     static class AbstractBaseImpl extends AbstractBase
135     {
136         protected Map<String,Object> props;
137 
AbstractBaseImpl(Map<String,Object> props)138         public AbstractBaseImpl(Map<String,Object> props) {
139             this.props = props;
140         }
141     }
142 
143     static interface Issue700Set extends java.util.Set<Object> { }
144 
145     static class Issue700Bean
146     {
147         protected Issue700Set item;
148 
149         @JsonCreator
Issue700Bean(@sonProperty"item") String item)150         public Issue700Bean(@JsonProperty("item") String item) { }
151 
getItem()152         public String getItem() { return null; }
153     }
154 
155     static final class MultiPropCreator1476 {
156         private final int intField;
157         private final String stringField;
158 
MultiPropCreator1476(@sonProperty"intField") int intField)159         public MultiPropCreator1476(@JsonProperty("intField") int intField) {
160           this(intField, "empty");
161         }
162 
MultiPropCreator1476(@sonProperty"stringField") String stringField)163         public MultiPropCreator1476(@JsonProperty("stringField") String stringField) {
164           this(-1, stringField);
165         }
166 
167         @JsonCreator
MultiPropCreator1476(@sonProperty"intField") int intField, @JsonProperty("stringField") String stringField)168         public MultiPropCreator1476(@JsonProperty("intField") int intField,
169                 @JsonProperty("stringField") String stringField) {
170           this.intField = intField;
171           this.stringField = stringField;
172         }
173 
getIntField()174         public int getIntField() {
175           return intField;
176         }
177 
getStringField()178         public String getStringField() {
179           return stringField;
180         }
181     }
182 
183     /*
184     /**********************************************************
185     /* Test methods
186     /**********************************************************
187      */
188 
189     private final ObjectMapper MAPPER = new ObjectMapper();
190 
testExceptionFromConstructor()191     public void testExceptionFromConstructor() throws Exception
192     {
193         try {
194             MAPPER.readValue("{}", BustedCtor.class);
195             fail("Expected exception");
196         } catch (ValueInstantiationException e) {
197             verifyException(e, ": foobar");
198             // also: should have nested exception
199             Throwable t = e.getCause();
200             if (t == null) {
201                 fail("Should have assigned cause for: ("+e.getClass().getSimpleName()+") "+e);
202             }
203             assertNotNull(t);
204             assertEquals(IllegalArgumentException.class, t.getClass());
205             assertEquals("foobar", t.getMessage());
206         } catch (Exception e) {
207             fail("Should have caught ValueInstantiationException, got: "+e);
208         }
209     }
210 
testSimpleConstructor()211     public void testSimpleConstructor() throws Exception
212     {
213         HashTest test = MAPPER.readValue("{\"type\":\"custom\",\"bytes\":\"abc\" }", HashTest.class);
214         assertEquals("custom", test.type);
215         assertEquals("abc", new String(test.bytes, "UTF-8"));
216     }
217 
testMissingPrimitives()218     public void testMissingPrimitives() throws Exception
219     {
220         Primitives p = MAPPER.readValue("{}", Primitives.class);
221         assertFalse(p.b);
222         assertEquals(0, p.x);
223         assertEquals(0.0, p.d);
224     }
225 
testJackson431()226     public void testJackson431() throws Exception
227     {
228         final Test431Container foo = MAPPER.readValue(
229                 "{\"items\":\n"
230                 +"[{\"bar\": 0,\n"
231                 +"\"id\": \"id123\",\n"
232                 +"\"foo\": 1\n"
233                 +"}]}",
234                 Test431Container.class);
235         assertNotNull(foo);
236     }
237 
238     // Catch and re-throw exceptions that Creator methods throw
testJackson438()239     public void testJackson438() throws Exception
240     {
241         Exception e = null;
242         try {
243             MAPPER.readValue("{ \"name\":\"foobar\" }", BeanFor438.class);
244             fail("Should have failed");
245         } catch (JsonMappingException e0) {
246             e = e0;
247         }
248         if (!(e instanceof ValueInstantiationException)) {
249             fail("Should have received ValueInstantiationException, caught "+e.getClass().getName());
250         }
251         verifyException(e, "don't like that name");
252         // Ok: also, let's ensure root cause is directly linked, without other extra wrapping:
253         Throwable t = e.getCause();
254         if (t == null) {
255             fail("Should have assigned cause for: ("+e.getClass().getSimpleName()+") "+e);
256         }
257         assertEquals(IllegalArgumentException.class, t.getClass());
258         verifyException(e, "don't like that name");
259     }
260 
testCreatorWithDupNames()261     public void testCreatorWithDupNames() throws Exception
262     {
263         try {
264             MAPPER.readValue("{\"bar\":\"x\"}", BrokenCreatorBean.class);
265             fail("Should have caught duplicate creator parameters");
266         } catch (InvalidDefinitionException e) {
267             verifyException(e, "duplicate creator property \"bar\"");
268             verifyException(e, "for type `com.fasterxml.jackson.databind.");
269             verifyException(e, "$BrokenCreatorBean`");
270         }
271     }
272 
testCreatorMultipleArgumentWithoutAnnotation()273     public void testCreatorMultipleArgumentWithoutAnnotation() throws Exception {
274         AutoDetectConstructorBean value = MAPPER.readValue("{\"bar\":\"bar\",\"foo\":\"foo\"}",
275                 AutoDetectConstructorBean.class);
276         assertEquals("bar", value.bar);
277         assertEquals("foo", value.foo);
278     }
279 
testIgnoredSingleArgCtor()280     public void testIgnoredSingleArgCtor() throws Exception
281     {
282         try {
283             MAPPER.readValue(quote("abc"), IgnoredCtor.class);
284             fail("Should have caught missing constructor problem");
285         } catch (MismatchedInputException e) {
286             verifyException(e, "no String-argument constructor/factory method");
287         }
288     }
289 
testAbstractFactory()290     public void testAbstractFactory() throws Exception
291     {
292         AbstractBase bean = MAPPER.readValue("{\"a\":3}", AbstractBase.class);
293         assertNotNull(bean);
294         AbstractBaseImpl impl = (AbstractBaseImpl) bean;
295         assertEquals(1, impl.props.size());
296         assertEquals(Integer.valueOf(3), impl.props.get("a"));
297     }
298 
testCreatorProperties()299     public void testCreatorProperties() throws Exception
300     {
301         Issue700Bean value = MAPPER.readValue("{ \"item\" : \"foo\" }", Issue700Bean.class);
302         assertNotNull(value);
303     }
304 
305     // [databind#1476]
testConstructorChoice()306     public void testConstructorChoice() throws Exception {
307         ObjectMapper mapper = new ObjectMapper();
308         MultiPropCreator1476 pojo = mapper.readValue("{ \"intField\": 1, \"stringField\": \"foo\" }",
309                 MultiPropCreator1476.class);
310         assertEquals(1, pojo.getIntField());
311         assertEquals("foo", pojo.getStringField());
312     }
313 }
314