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