package com.fasterxml.jackson.databind.creators; import java.io.IOException; import java.util.List; import java.util.Map; import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; public class TestCreators2 extends BaseMapTest { static class HashTest { final byte[] bytes; final String type; @JsonCreator public HashTest(@JsonProperty("bytes") @JsonDeserialize(using = BytesDeserializer.class) final byte[] bytes, @JsonProperty("type") final String type) { this.bytes = bytes; this.type = type; } } static class BytesDeserializer extends JsonDeserializer<byte[]> { @Override public byte[] deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { String str = jp.getText(); return str.getBytes("UTF-8"); } } static class Primitives { protected int x = 3; protected double d = -0.5; protected boolean b = true; @JsonCreator public Primitives(@JsonProperty("x") int x, @JsonProperty("d") double d, @JsonProperty("b") boolean b) { this.x = x; this.d = d; this.b = b; } } protected static class Test431Container { protected final List<Item431> items; @JsonCreator public Test431Container(@JsonProperty("items") final List<Item431> i) { items = i; } } @JsonIgnoreProperties(ignoreUnknown = true) protected static class Item431 { protected final String id; @JsonCreator public Item431(@JsonProperty("id") String id) { this.id = id; } } // Test class for verifying that creator-call failures are reported as checked exceptions static class BeanFor438 { @JsonCreator public BeanFor438(@JsonProperty("name") String s) { throw new IllegalArgumentException("I don't like that name!"); } } // For [JACKSON-470]: should be appropriately detected, reported error about static class BrokenCreatorBean { protected String bar; @JsonCreator public BrokenCreatorBean(@JsonProperty("bar") String bar1, @JsonProperty("bar") String bar2) { bar = ""+bar1+"/"+bar2; } } // For [JACKSON-541]: should not need @JsonCreator if SerializationFeature.AUTO_DETECT_CREATORS is on. static class AutoDetectConstructorBean { protected final String foo; protected final String bar; public AutoDetectConstructorBean(@JsonProperty("bar") String bar, @JsonProperty("foo") String foo){ this.bar = bar; this.foo = foo; } } static class BustedCtor { @JsonCreator BustedCtor(@JsonProperty("a") String value) { throw new IllegalArgumentException("foobar"); } } static class IgnoredCtor { @JsonIgnore public IgnoredCtor(String arg) { throw new RuntimeException("Should never use this constructor"); } public IgnoredCtor() { } } abstract static class AbstractBase { @JsonCreator public static AbstractBase create(Map<String,Object> props) { return new AbstractBaseImpl(props); } } static class AbstractBaseImpl extends AbstractBase { protected Map<String,Object> props; public AbstractBaseImpl(Map<String,Object> props) { this.props = props; } } static interface Issue700Set extends java.util.Set<Object> { } static class Issue700Bean { protected Issue700Set item; @JsonCreator public Issue700Bean(@JsonProperty("item") String item) { } public String getItem() { return null; } } static final class MultiPropCreator1476 { private final int intField; private final String stringField; public MultiPropCreator1476(@JsonProperty("intField") int intField) { this(intField, "empty"); } public MultiPropCreator1476(@JsonProperty("stringField") String stringField) { this(-1, stringField); } @JsonCreator public MultiPropCreator1476(@JsonProperty("intField") int intField, @JsonProperty("stringField") String stringField) { this.intField = intField; this.stringField = stringField; } public int getIntField() { return intField; } public String getStringField() { return stringField; } } /* /********************************************************** /* Test methods /********************************************************** */ private final ObjectMapper MAPPER = new ObjectMapper(); public void testExceptionFromConstructor() throws Exception { try { MAPPER.readValue("{}", BustedCtor.class); fail("Expected exception"); } catch (JsonMappingException e) { verifyException(e, ": foobar"); // also: should have nested exception Throwable t = e.getCause(); if (t == null) { fail("Should have assigned cause for: ("+e.getClass().getSimpleName()+") "+e); } assertNotNull(t); assertEquals(IllegalArgumentException.class, t.getClass()); assertEquals("foobar", t.getMessage()); } } public void testSimpleConstructor() throws Exception { HashTest test = MAPPER.readValue("{\"type\":\"custom\",\"bytes\":\"abc\" }", HashTest.class); assertEquals("custom", test.type); assertEquals("abc", new String(test.bytes, "UTF-8")); } // Test for [JACKSON-372] public void testMissingPrimitives() throws Exception { Primitives p = MAPPER.readValue("{}", Primitives.class); assertFalse(p.b); assertEquals(0, p.x); assertEquals(0.0, p.d); } public void testJackson431() throws Exception { final Test431Container foo = MAPPER.readValue( "{\"items\":\n" +"[{\"bar\": 0,\n" +"\"id\": \"id123\",\n" +"\"foo\": 1\n" +"}]}", Test431Container.class); assertNotNull(foo); } // Catch and re-throw exceptions that Creator methods throw public void testJackson438() throws Exception { Exception e = null; try { MAPPER.readValue("{ \"name\":\"foobar\" }", BeanFor438.class); fail("Should have failed"); } catch (Exception e0) { e = e0; } if (!(e instanceof JsonMappingException)) { fail("Should have received JsonMappingException, caught "+e.getClass().getName()); } verifyException(e, "don't like that name"); // Ok: also, let's ensure root cause is directly linked, without other extra wrapping: Throwable t = e.getCause(); if (t == null) { fail("Should have assigned cause for: ("+e.getClass().getSimpleName()+") "+e); } assertEquals(IllegalArgumentException.class, t.getClass()); verifyException(e, "don't like that name"); } public void testCreatorWithDupNames() throws Exception { try { MAPPER.readValue("{\"bar\":\"x\"}", BrokenCreatorBean.class); fail("Should have caught duplicate creator parameters"); } catch (JsonMappingException e) { verifyException(e, "duplicate creator property \"bar\""); } } public void testCreatorMultipleArgumentWithoutAnnotation() throws Exception { AutoDetectConstructorBean value = MAPPER.readValue("{\"bar\":\"bar\",\"foo\":\"foo\"}", AutoDetectConstructorBean.class); assertEquals("bar", value.bar); assertEquals("foo", value.foo); } public void testIgnoredSingleArgCtor() throws Exception { try { MAPPER.readValue(quote("abc"), IgnoredCtor.class); fail("Should have caught missing constructor problem"); } catch (JsonMappingException e) { verifyException(e, "no String-argument constructor/factory method"); } } public void testAbstractFactory() throws Exception { AbstractBase bean = MAPPER.readValue("{\"a\":3}", AbstractBase.class); assertNotNull(bean); AbstractBaseImpl impl = (AbstractBaseImpl) bean; assertEquals(1, impl.props.size()); assertEquals(Integer.valueOf(3), impl.props.get("a")); } public void testCreatorProperties() throws Exception { Issue700Bean value = MAPPER.readValue("{ \"item\" : \"foo\" }", Issue700Bean.class); assertNotNull(value); } // [databind#1476] public void testConstructorChoice() throws Exception { ObjectMapper mapper = new ObjectMapper(); MultiPropCreator1476 pojo = mapper.readValue("{ \"intField\": 1, \"stringField\": \"foo\" }", MultiPropCreator1476.class); assertEquals(1, pojo.getIntField()); assertEquals("foo", pojo.getStringField()); } }