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