package com.fasterxml.jackson.databind.convert; import java.util.LinkedHashMap; import java.util.Map; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.util.StdConverter; /** * Tests for various conversions, especially ones using * {@link ObjectMapper#convertValue(Object, Class)}. */ public class TestBeanConversions extends com.fasterxml.jackson.databind.BaseMapTest { static class PointZ { public int x, y; public int z = -13; public PointZ() { } public PointZ(int a, int b, int c) { x = a; y = b; z = c; } } static class PointStrings { public final String x, y; public PointStrings(String x, String y) { this.x = x; this.y = y; } } public static class BooleanBean { public boolean boolProp; } static class WrapperBean { public BooleanBean x; } static class ObjectWrapper { private Object data; public ObjectWrapper() { } public ObjectWrapper(Object o) { data = o; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } } static class Leaf { public int value; public Leaf() { } public Leaf(int v) { value = v; } } // [databind#288] @JsonSerialize(converter = ConvertingBeanConverter.class) static class ConvertingBean { public int x, y; public ConvertingBean(int v1, int v2) { x = v1; y = v2; } } public static class DummyBean { public final int a, b; public DummyBean(int v1, int v2) { a = v1 * 2; b = v2 * 2; } } static class ConvertingBeanConverter extends StdConverter<ConvertingBean, DummyBean> { @Override public DummyBean convert(ConvertingBean cb) { return new DummyBean(cb.x, cb.y); } } @JsonDeserialize(using = NullBeanDeserializer.class) static class NullBean { public static final NullBean NULL_INSTANCE = new NullBean(); } static class NullBeanDeserializer extends JsonDeserializer<NullBean> { @Override public NullBean getNullValue(final DeserializationContext context) { return NullBean.NULL_INSTANCE; } @Override public NullBean deserialize(final JsonParser parser, final DeserializationContext context) { throw new UnsupportedOperationException(); } } /* /********************************************************** /* Test methods /********************************************************** */ private final ObjectMapper MAPPER = new ObjectMapper(); public void testBeanConvert() { // should have no problems convert between compatible beans... PointStrings input = new PointStrings("37", "-9"); PointZ point = MAPPER.convertValue(input, PointZ.class); assertEquals(37, point.x); assertEquals(-9, point.y); // z not included in input, will be whatever default constructor provides assertEquals(-13, point.z); } // For [JACKSON-371]; verify that we know property that caused issue... // (note: not optimal place for test, but will have to do for now) public void testErrorReporting() throws Exception { //String json = "{\"boolProp\":\"oops\"}"; // First: unknown property try { MAPPER.readValue("{\"unknownProp\":true}", BooleanBean.class); } catch (JsonProcessingException e) { verifyException(e, "unknownProp"); } // then bad conversion try { MAPPER.readValue("{\"boolProp\":\"foobar\"}", BooleanBean.class); } catch (JsonMappingException e) { verifyException(e, "Can not deserialize value of type boolean from String"); } } public void testIssue458() throws Exception { ObjectWrapper a = new ObjectWrapper("foo"); ObjectWrapper b = new ObjectWrapper(a); ObjectWrapper b2 = MAPPER.convertValue(b, ObjectWrapper.class); ObjectWrapper a2 = MAPPER.convertValue(b2.getData(), ObjectWrapper.class); assertEquals("foo", a2.getData()); } // should work regardless of wrapping... public void testWrapping() throws Exception { ObjectMapper wrappingMapper = new ObjectMapper(); wrappingMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); wrappingMapper.enable(SerializationFeature.WRAP_ROOT_VALUE); // conversion is ok, even if it's bogus one _convertAndVerifyPoint(wrappingMapper); // also: ok to have mismatched settings, since as per [JACKSON-710], should // not actually use wrapping internally in these cases wrappingMapper = new ObjectMapper(); wrappingMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); wrappingMapper.disable(SerializationFeature.WRAP_ROOT_VALUE); _convertAndVerifyPoint(wrappingMapper); wrappingMapper = new ObjectMapper(); wrappingMapper.disable(DeserializationFeature.UNWRAP_ROOT_VALUE); wrappingMapper.enable(SerializationFeature.WRAP_ROOT_VALUE); _convertAndVerifyPoint(wrappingMapper); } // [Issue-11]: simple cast, for POJOs etc public void testConvertUsingCast() throws Exception { String str = new String("foo"); CharSequence seq = str; String result = MAPPER.convertValue(seq, String.class); // should just cast... assertSame(str, result); } // [Issue-11]: simple cast, for Tree public void testNodeConvert() throws Exception { ObjectNode src = (ObjectNode) MAPPER.readTree("{}"); TreeNode node = src; ObjectNode result = MAPPER.treeToValue(node, ObjectNode.class); // should just cast... assertSame(src, result); } private void _convertAndVerifyPoint(ObjectMapper m) { final PointZ input = new PointZ(1, 2, 3); PointZ output = m.convertValue(input, PointZ.class); assertEquals(1, output.x); assertEquals(2, output.y); assertEquals(3, output.z); } /** * Need to test "shortcuts" introduced by [databind#11] */ public void testIssue11() throws Exception { // First the expected use case, Node specification ObjectNode root = MAPPER.createObjectNode(); JsonNode n = root; ObjectNode ob2 = MAPPER.convertValue(n, ObjectNode.class); assertSame(root, ob2); JsonNode n2 = MAPPER.convertValue(n, JsonNode.class); assertSame(root, n2); // then some other no-op conversions String STR = "test"; CharSequence seq = MAPPER.convertValue(STR, CharSequence.class); assertSame(STR, seq); // and then something that should NOT use short-cut Leaf l = new Leaf(13); Map<?,?> m = MAPPER.convertValue(l, Map.class); assertNotNull(m); assertEquals(1, m.size()); assertEquals(Integer.valueOf(13), m.get("value")); // and reverse too Leaf l2 = MAPPER.convertValue(m, Leaf.class); assertEquals(13, l2.value); // also; ok to use "untyped" (Object): Object ob = MAPPER.convertValue(l, Object.class); assertNotNull(ob); assertEquals(LinkedHashMap.class, ob.getClass()); // And one more: this time with a minor twist final Object plaino = new Object(); // first, a failed attempt: try { m = MAPPER.convertValue(plaino, Map.class); fail("Conversion should have failed"); } catch (IllegalArgumentException e) { verifyException(e, "no properties discovered"); } ObjectMapper mapper = new ObjectMapper(); mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); try { assertEquals("{}", mapper.writeValueAsString(plaino)); } catch (Exception e) { throw (Exception) e.getCause(); } // should now work, via serialization/deserialization: m = mapper.convertValue(plaino, Map.class); assertNotNull(m); assertEquals(0, m.size()); } public void testConversionIssue288() throws Exception { String json = MAPPER.writeValueAsString(new ConvertingBean(1, 2)); // must be {"a":2,"b":4} assertEquals("{\"a\":2,\"b\":4}", json); } // Test null conversions from [databind#1433] public void testConversionIssue1433() throws Exception { assertNull(MAPPER.convertValue(null, Object.class)); assertNull(MAPPER.convertValue(null, PointZ.class)); assertSame(NullBean.NULL_INSTANCE, MAPPER.convertValue(null, NullBean.class)); } }