1 package com.fasterxml.jackson.databind.node; 2 3 import java.io.IOException; 4 import java.math.BigDecimal; 5 import java.util.*; 6 7 import static org.junit.Assert.*; 8 9 import org.junit.Assert; 10 11 import com.fasterxml.jackson.annotation.JsonTypeInfo; 12 import com.fasterxml.jackson.core.*; 13 import com.fasterxml.jackson.core.type.WritableTypeId; 14 import com.fasterxml.jackson.databind.*; 15 import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 16 import com.fasterxml.jackson.databind.annotation.JsonSerialize; 17 import com.fasterxml.jackson.databind.jsontype.TypeSerializer; 18 import com.fasterxml.jackson.databind.util.TokenBuffer; 19 20 /** 21 * Unit tests for verifying functionality of {@link JsonNode} methods that 22 * convert values to other types 23 */ 24 public class TestConversions extends BaseMapTest 25 { 26 static class Root { 27 public Leaf leaf; 28 } 29 30 static class Leaf { 31 public int value; 32 Leaf()33 public Leaf() { } Leaf(int v)34 public Leaf(int v) { value = v; } 35 } 36 37 @JsonDeserialize(using = LeafDeserializer.class) 38 public static class LeafMixIn { } 39 40 // for [databind#467] 41 @JsonSerialize(using=Issue467Serializer.class) 42 static class Issue467Bean { 43 public int i; 44 Issue467Bean(int i0)45 public Issue467Bean(int i0) { i = i0; } Issue467Bean()46 public Issue467Bean() { this(0); } 47 } 48 49 @JsonSerialize(using=Issue467TreeSerializer.class) 50 static class Issue467Tree { 51 } 52 53 static class Issue467Serializer extends JsonSerializer<Issue467Bean> { 54 @Override serialize(Issue467Bean value, JsonGenerator jgen, SerializerProvider provider)55 public void serialize(Issue467Bean value, JsonGenerator jgen, 56 SerializerProvider provider) throws IOException { 57 jgen.writeObject(new Issue467TmpBean(value.i)); 58 } 59 } 60 61 static class Issue467TreeSerializer extends JsonSerializer<Issue467Tree> { 62 @Override serialize(Issue467Tree value, JsonGenerator jgen, SerializerProvider provider)63 public void serialize(Issue467Tree value, JsonGenerator jgen, 64 SerializerProvider provider) throws IOException { 65 jgen.writeTree(BooleanNode.TRUE); 66 } 67 } 68 69 static class Issue467TmpBean { 70 public int x; 71 Issue467TmpBean(int i)72 public Issue467TmpBean(int i) { x = i; } 73 } 74 75 static class Issue709Bean { 76 public byte[] data; 77 } 78 79 @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="_class") 80 static class LongContainer1940 { 81 public Long longObj; 82 } 83 84 // [databind#433] 85 static class CustomSerializedPojo implements JsonSerializable 86 { 87 private final ObjectNode node = JsonNodeFactory.instance.objectNode(); 88 setFoo(final String foo)89 public void setFoo(final String foo) { 90 node.put("foo", foo); 91 } 92 93 @Override serialize(final JsonGenerator jgen, final SerializerProvider provider)94 public void serialize(final JsonGenerator jgen, final SerializerProvider provider) throws IOException 95 { 96 jgen.writeTree(node); 97 } 98 99 @Override serializeWithType(JsonGenerator g, SerializerProvider provider, TypeSerializer typeSer)100 public void serializeWithType(JsonGenerator g, 101 SerializerProvider provider, TypeSerializer typeSer) throws IOException 102 { 103 WritableTypeId typeIdDef = new WritableTypeId(this, JsonToken.START_OBJECT); 104 typeSer.writeTypePrefix(g, typeIdDef); 105 serialize(g, provider); 106 typeSer.writeTypePrefix(g, typeIdDef); 107 } 108 } 109 110 /* 111 /********************************************************** 112 /* Unit tests 113 /********************************************************** 114 */ 115 116 private final ObjectMapper MAPPER = objectMapper(); 117 testAsInt()118 public void testAsInt() throws Exception 119 { 120 assertEquals(9, IntNode.valueOf(9).asInt()); 121 assertEquals(7, LongNode.valueOf(7L).asInt()); 122 assertEquals(13, new TextNode("13").asInt()); 123 assertEquals(0, new TextNode("foobar").asInt()); 124 assertEquals(27, new TextNode("foobar").asInt(27)); 125 assertEquals(1, BooleanNode.TRUE.asInt()); 126 } 127 testAsBoolean()128 public void testAsBoolean() throws Exception 129 { 130 assertEquals(false, BooleanNode.FALSE.asBoolean()); 131 assertEquals(true, BooleanNode.TRUE.asBoolean()); 132 assertEquals(false, IntNode.valueOf(0).asBoolean()); 133 assertEquals(true, IntNode.valueOf(1).asBoolean()); 134 assertEquals(false, LongNode.valueOf(0).asBoolean()); 135 assertEquals(true, LongNode.valueOf(-34L).asBoolean()); 136 assertEquals(true, new TextNode("true").asBoolean()); 137 assertEquals(false, new TextNode("false").asBoolean()); 138 assertEquals(false, new TextNode("barf").asBoolean()); 139 assertEquals(true, new TextNode("barf").asBoolean(true)); 140 141 assertEquals(true, new POJONode(Boolean.TRUE).asBoolean()); 142 } 143 144 // Deserializer to trigger the problem described in [JACKSON-554] 145 public static class LeafDeserializer extends JsonDeserializer<Leaf> 146 { 147 @Override deserialize(JsonParser jp, DeserializationContext ctxt)148 public Leaf deserialize(JsonParser jp, DeserializationContext ctxt) 149 throws IOException, JsonProcessingException 150 { 151 JsonNode tree = (JsonNode) jp.readValueAsTree(); 152 Leaf leaf = new Leaf(); 153 leaf.value = tree.get("value").intValue(); 154 return leaf; 155 } 156 } 157 testTreeToValue()158 public void testTreeToValue() throws Exception 159 { 160 String JSON = "{\"leaf\":{\"value\":13}}"; 161 ObjectMapper mapper = new ObjectMapper(); 162 mapper.addMixIn(Leaf.class, LeafMixIn.class); 163 JsonNode root = mapper.readTree(JSON); 164 // Ok, try converting to bean using two mechanisms 165 Root r1 = mapper.treeToValue(root, Root.class); 166 assertNotNull(r1); 167 assertEquals(13, r1.leaf.value); 168 } 169 170 // [databind#1208]: should coerce POJOs at least at root level testTreeToValueWithPOJO()171 public void testTreeToValueWithPOJO() throws Exception 172 { 173 Calendar c = Calendar.getInstance(); 174 c.setTime(new java.util.Date(0)); 175 ValueNode pojoNode = MAPPER.getNodeFactory().pojoNode(c); 176 Calendar result = MAPPER.treeToValue(pojoNode, Calendar.class); 177 assertNotNull(result); 178 assertEquals(result.getTimeInMillis(), c.getTimeInMillis()); 179 } 180 testBase64Text()181 public void testBase64Text() throws Exception 182 { 183 // let's actually iterate over sets of encoding modes, lengths 184 185 final int[] LENS = { 1, 2, 3, 4, 7, 9, 32, 33, 34, 35 }; 186 final Base64Variant[] VARIANTS = { 187 Base64Variants.MIME, 188 Base64Variants.MIME_NO_LINEFEEDS, 189 Base64Variants.MODIFIED_FOR_URL, 190 Base64Variants.PEM 191 }; 192 193 for (int len : LENS) { 194 byte[] input = new byte[len]; 195 for (int i = 0; i < input.length; ++i) { 196 input[i] = (byte) i; 197 } 198 for (Base64Variant variant : VARIANTS) { 199 TextNode n = new TextNode(variant.encode(input)); 200 byte[] data = null; 201 try { 202 data = n.getBinaryValue(variant); 203 } catch (Exception e) { 204 fail("Failed (variant "+variant+", data length "+len+"): "+e.getMessage()); 205 } 206 assertNotNull(data); 207 assertArrayEquals(data, input); 208 209 // 15-Aug-2018, tatu: [databind#2096] requires another test 210 JsonParser p = new TreeTraversingParser(n); 211 assertEquals(JsonToken.VALUE_STRING, p.nextToken()); 212 try { 213 data = p.getBinaryValue(variant); 214 } catch (Exception e) { 215 fail("Failed (variant "+variant+", data length "+len+"): "+e.getMessage()); 216 } 217 assertNotNull(data); 218 assertArrayEquals(data, input); 219 p.close(); 220 } 221 } 222 } 223 224 /** 225 * Simple test to verify that byte[] values can be handled properly when 226 * converting, as long as there is metadata (from POJO definitions). 227 */ testIssue709()228 public void testIssue709() throws Exception 229 { 230 byte[] inputData = new byte[] { 1, 2, 3 }; 231 ObjectNode node = MAPPER.createObjectNode(); 232 node.put("data", inputData); 233 Issue709Bean result = MAPPER.treeToValue(node, Issue709Bean.class); 234 String json = MAPPER.writeValueAsString(node); 235 Issue709Bean resultFromString = MAPPER.readValue(json, Issue709Bean.class); 236 Issue709Bean resultFromConvert = MAPPER.convertValue(node, Issue709Bean.class); 237 238 // all methods should work equally well: 239 Assert.assertArrayEquals(inputData, resultFromString.data); 240 Assert.assertArrayEquals(inputData, resultFromConvert.data); 241 Assert.assertArrayEquals(inputData, result.data); 242 } 243 testEmbeddedByteArray()244 public void testEmbeddedByteArray() throws Exception 245 { 246 TokenBuffer buf = new TokenBuffer(MAPPER, false); 247 buf.writeObject(new byte[3]); 248 JsonNode node = MAPPER.readTree(buf.asParser()); 249 buf.close(); 250 assertTrue(node.isBinary()); 251 byte[] data = node.binaryValue(); 252 assertNotNull(data); 253 assertEquals(3, data.length); 254 } 255 256 // [databind#232] testBigDecimalAsPlainStringTreeConversion()257 public void testBigDecimalAsPlainStringTreeConversion() throws Exception 258 { 259 ObjectMapper mapper = new ObjectMapper(); 260 mapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); 261 Map<String, Object> map = new HashMap<String, Object>(); 262 String PI_STR = "3.00000000"; 263 map.put("pi", new BigDecimal(PI_STR)); 264 JsonNode tree = mapper.valueToTree(map); 265 assertNotNull(tree); 266 assertEquals(1, tree.size()); 267 assertTrue(tree.has("pi")); 268 } 269 270 // [databind#433] testBeanToTree()271 public void testBeanToTree() throws Exception 272 { 273 final CustomSerializedPojo pojo = new CustomSerializedPojo(); 274 pojo.setFoo("bar"); 275 final JsonNode node = MAPPER.valueToTree(pojo); 276 assertEquals(JsonNodeType.OBJECT, node.getNodeType()); 277 } 278 279 // [databind#467] testConversionOfPojos()280 public void testConversionOfPojos() throws Exception 281 { 282 final Issue467Bean input = new Issue467Bean(13); 283 final String EXP = "{\"x\":13}"; 284 285 // first, sanity check 286 String json = MAPPER.writeValueAsString(input); 287 assertEquals(EXP, json); 288 289 // then via conversions: should become JSON Object 290 JsonNode tree = MAPPER.valueToTree(input); 291 assertTrue("Expected Object, got "+tree.getNodeType(), tree.isObject()); 292 assertEquals(EXP, MAPPER.writeValueAsString(tree)); 293 } 294 295 // [databind#467] testConversionOfTrees()296 public void testConversionOfTrees() throws Exception 297 { 298 final Issue467Tree input = new Issue467Tree(); 299 final String EXP = "true"; 300 301 // first, sanity check 302 String json = MAPPER.writeValueAsString(input); 303 assertEquals(EXP, json); 304 305 // then via conversions: should become JSON Object 306 JsonNode tree = MAPPER.valueToTree(input); 307 assertTrue("Expected Object, got "+tree.getNodeType(), tree.isBoolean()); 308 assertEquals(EXP, MAPPER.writeValueAsString(tree)); 309 } 310 311 // [databind#1940]: losing of precision due to coercion testBufferedLongViaCoercion()312 public void testBufferedLongViaCoercion() throws Exception { 313 long EXP = 1519348261000L; 314 JsonNode tree = MAPPER.readTree("{\"longObj\": "+EXP+".0, \"_class\": \""+LongContainer1940.class.getName()+"\"}"); 315 LongContainer1940 obj = MAPPER.treeToValue(tree, LongContainer1940.class); 316 assertEquals(Long.valueOf(EXP), obj.longObj); 317 } 318 testConversionsOfNull()319 public void testConversionsOfNull() 320 { 321 // First: `null` value should become `NullNode` 322 JsonNode n = MAPPER.valueToTree(null); 323 assertNotNull(n); 324 assertTrue(n.isNull()); 325 326 // and vice versa 327 Object pojo = MAPPER.treeToValue(n, Root.class); 328 assertNull(pojo); 329 } 330 } 331