1 package com.fasterxml.jackson.databind.convert;
2 
3 import java.util.LinkedHashMap;
4 import java.util.Map;
5 
6 import com.fasterxml.jackson.core.JsonParser;
7 import com.fasterxml.jackson.core.JsonProcessingException;
8 import com.fasterxml.jackson.core.TreeNode;
9 import com.fasterxml.jackson.databind.*;
10 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
11 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
12 import com.fasterxml.jackson.databind.node.ObjectNode;
13 import com.fasterxml.jackson.databind.util.StdConverter;
14 
15 /**
16  * Tests for various conversions, especially ones using
17  * {@link ObjectMapper#convertValue(Object, Class)}.
18  */
19 public class TestBeanConversions
20     extends com.fasterxml.jackson.databind.BaseMapTest
21 {
22     static class PointZ {
23         public int x, y;
24 
25         public int z = -13;
26 
PointZ()27         public PointZ() { }
PointZ(int a, int b, int c)28         public PointZ(int a, int b, int c)
29         {
30             x = a;
31             y = b;
32             z = c;
33         }
34     }
35 
36     static class PointStrings {
37         public final String x, y;
38 
PointStrings(String x, String y)39         public PointStrings(String x, String y) {
40             this.x = x;
41             this.y = y;
42         }
43     }
44 
45     public static class BooleanBean {
46         public boolean boolProp;
47     }
48 
49     static class WrapperBean {
50         public BooleanBean x;
51     }
52 
53     static class ObjectWrapper
54     {
55         private Object data;
56 
ObjectWrapper()57         public ObjectWrapper() { }
ObjectWrapper(Object o)58         public ObjectWrapper(Object o) { data = o; }
59 
getData()60         public Object getData() { return data; }
setData(Object data)61         public void setData(Object data) { this.data = data; }
62     }
63 
64     static class Leaf {
65         public int value;
66 
Leaf()67         public Leaf() { }
Leaf(int v)68         public Leaf(int v) { value = v; }
69     }
70 
71     // [databind#288]
72 
73     @JsonSerialize(converter = ConvertingBeanConverter.class)
74     static class ConvertingBean {
75        public int x, y;
ConvertingBean(int v1, int v2)76        public ConvertingBean(int v1, int v2) {
77           x = v1;
78           y = v2;
79        }
80     }
81 
82     public static class DummyBean {
83        public final int a, b;
DummyBean(int v1, int v2)84        public DummyBean(int v1, int v2) {
85           a = v1 * 2;
86           b = v2 * 2;
87        }
88     }
89 
90     static class ConvertingBeanConverter extends StdConverter<ConvertingBean, DummyBean>
91     {
92        @Override
convert(ConvertingBean cb)93        public DummyBean convert(ConvertingBean cb) {
94           return new DummyBean(cb.x, cb.y);
95        }
96     }
97 
98     @JsonDeserialize(using = NullBeanDeserializer.class)
99     static class NullBean {
100         public static final NullBean NULL_INSTANCE = new NullBean();
101     }
102 
103     static class NullBeanDeserializer extends JsonDeserializer<NullBean> {
104         @Override
getNullValue(final DeserializationContext context)105         public NullBean getNullValue(final DeserializationContext context) {
106             return NullBean.NULL_INSTANCE;
107         }
108 
109         @Override
deserialize(final JsonParser parser, final DeserializationContext context)110         public NullBean deserialize(final JsonParser parser, final DeserializationContext context) {
111             throw new UnsupportedOperationException();
112         }
113     }
114 
115     /*
116     /**********************************************************
117     /* Test methods
118     /**********************************************************
119      */
120 
121     private final ObjectMapper MAPPER = new ObjectMapper();
122 
testBeanConvert()123     public void testBeanConvert()
124     {
125         // should have no problems convert between compatible beans...
126         PointStrings input = new PointStrings("37", "-9");
127         PointZ point = MAPPER.convertValue(input, PointZ.class);
128         assertEquals(37, point.x);
129         assertEquals(-9, point.y);
130         // z not included in input, will be whatever default constructor provides
131         assertEquals(-13, point.z);
132     }
133 
134     // For [JACKSON-371]; verify that we know property that caused issue...
135     // (note: not optimal place for test, but will have to do for now)
testErrorReporting()136     public void testErrorReporting() throws Exception
137     {
138         //String json = "{\"boolProp\":\"oops\"}";
139         // First: unknown property
140         try {
141             MAPPER.readValue("{\"unknownProp\":true}", BooleanBean.class);
142         } catch (JsonProcessingException e) {
143             verifyException(e, "unknownProp");
144         }
145 
146         // then bad conversion
147         try {
148             MAPPER.readValue("{\"boolProp\":\"foobar\"}", BooleanBean.class);
149         } catch (JsonMappingException e) {
150             verifyException(e, "Cannot deserialize value of type `boolean` from String");
151         }
152     }
153 
testIssue458()154     public void testIssue458() throws Exception
155     {
156         ObjectWrapper a = new ObjectWrapper("foo");
157         ObjectWrapper b = new ObjectWrapper(a);
158         ObjectWrapper b2 = MAPPER.convertValue(b, ObjectWrapper.class);
159         ObjectWrapper a2 = MAPPER.convertValue(b2.getData(), ObjectWrapper.class);
160         assertEquals("foo", a2.getData());
161     }
162 
163     // should work regardless of wrapping...
testWrapping()164     public void testWrapping() throws Exception
165     {
166         ObjectMapper wrappingMapper = new ObjectMapper();
167         wrappingMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
168         wrappingMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
169 
170         // conversion is ok, even if it's bogus one
171         _convertAndVerifyPoint(wrappingMapper);
172 
173         // also: ok to have mismatched settings, since as per [JACKSON-710], should
174         // not actually use wrapping internally in these cases
175         wrappingMapper = new ObjectMapper();
176         wrappingMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
177         wrappingMapper.disable(SerializationFeature.WRAP_ROOT_VALUE);
178         _convertAndVerifyPoint(wrappingMapper);
179 
180         wrappingMapper = new ObjectMapper();
181         wrappingMapper.disable(DeserializationFeature.UNWRAP_ROOT_VALUE);
182         wrappingMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
183         _convertAndVerifyPoint(wrappingMapper);
184     }
185 
186     // [Issue-11]: simple cast, for POJOs etc
testConvertUsingCast()187     public void testConvertUsingCast() throws Exception
188     {
189         String str = new String("foo");
190         CharSequence seq = str;
191         String result = MAPPER.convertValue(seq, String.class);
192         // should just cast...
193         assertSame(str, result);
194     }
195 
196     // [Issue-11]: simple cast, for Tree
testNodeConvert()197     public void testNodeConvert() throws Exception
198     {
199         ObjectNode src = (ObjectNode) MAPPER.readTree("{}");
200         TreeNode node = src;
201         ObjectNode result = MAPPER.treeToValue(node, ObjectNode.class);
202         // should just cast...
203         assertSame(src, result);
204     }
205 
_convertAndVerifyPoint(ObjectMapper m)206     private void _convertAndVerifyPoint(ObjectMapper m)
207     {
208         final PointZ input = new PointZ(1, 2, 3);
209         PointZ output = m.convertValue(input, PointZ.class);
210         assertEquals(1, output.x);
211         assertEquals(2, output.y);
212         assertEquals(3, output.z);
213     }
214 
215     /**
216      * Need to test "shortcuts" introduced by [databind#11]
217      */
testIssue11()218     public void testIssue11() throws Exception
219     {
220         // then some other no-op conversions
221         StringBuilder SB = new StringBuilder("test");
222         CharSequence seq = MAPPER.convertValue(SB, CharSequence.class);
223         assertNotSame(SB, seq);
224 
225         // and then something that should NOT use short-cut
226         Leaf l = new Leaf(13);
227         Map<?,?> m = MAPPER.convertValue(l, Map.class);
228         assertNotNull(m);
229         assertEquals(1, m.size());
230         assertEquals(Integer.valueOf(13), m.get("value"));
231 
232         // and reverse too
233         Leaf l2 = MAPPER.convertValue(m, Leaf.class);
234         assertEquals(13, l2.value);
235 
236         // also; ok to use "untyped" (Object):
237         Object ob = MAPPER.convertValue(l, Object.class);
238         assertNotNull(ob);
239         assertEquals(LinkedHashMap.class, ob.getClass());
240 
241         // And one more: this time with a minor twist
242         final Object plaino = new Object();
243         // first, a failed attempt:
244         try {
245             m = MAPPER.convertValue(plaino, Map.class);
246             fail("Conversion should have failed");
247         } catch (IllegalArgumentException e) {
248             verifyException(e, "no properties discovered");
249         }
250 
251         ObjectMapper mapper = new ObjectMapper();
252         mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
253         try {
254             assertEquals("{}", mapper.writeValueAsString(plaino));
255         } catch (Exception e) {
256             throw (Exception) e.getCause();
257         }
258         // should now work, via serialization/deserialization:
259         m = mapper.convertValue(plaino, Map.class);
260         assertNotNull(m);
261         assertEquals(0, m.size());
262     }
263 
testConversionIssue288()264     public void testConversionIssue288() throws Exception
265     {
266         String json = MAPPER.writeValueAsString(new ConvertingBean(1, 2));
267         // must be  {"a":2,"b":4}
268         assertEquals("{\"a\":2,\"b\":4}", json);
269     }
270 
271     // Test null conversions from [databind#1433]
testConversionIssue1433()272     public void testConversionIssue1433() throws Exception
273     {
274         assertNull(MAPPER.convertValue(null, Object.class));
275         assertNull(MAPPER.convertValue(null, PointZ.class));
276 
277         assertSame(NullBean.NULL_INSTANCE,
278                 MAPPER.convertValue(null, NullBean.class));
279     }
280 }
281