package com.fasterxml.jackson.databind.node; import java.io.IOException; import java.math.BigDecimal; import java.util.*; import static org.junit.Assert.*; import org.junit.Assert; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.type.WritableTypeId; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.util.TokenBuffer; /** * Unit tests for verifying functionality of {@link JsonNode} methods that * convert values to other types */ public class TestConversions extends BaseMapTest { static class Root { public Leaf leaf; } static class Leaf { public int value; public Leaf() { } public Leaf(int v) { value = v; } } @JsonDeserialize(using = LeafDeserializer.class) public static class LeafMixIn { } // for [databind#467] @JsonSerialize(using=Issue467Serializer.class) static class Issue467Bean { public int i; public Issue467Bean(int i0) { i = i0; } public Issue467Bean() { this(0); } } @JsonSerialize(using=Issue467TreeSerializer.class) static class Issue467Tree { } static class Issue467Serializer extends JsonSerializer { @Override public void serialize(Issue467Bean value, JsonGenerator jgen, SerializerProvider provider) throws IOException { jgen.writeObject(new Issue467TmpBean(value.i)); } } static class Issue467TreeSerializer extends JsonSerializer { @Override public void serialize(Issue467Tree value, JsonGenerator jgen, SerializerProvider provider) throws IOException { jgen.writeTree(BooleanNode.TRUE); } } static class Issue467TmpBean { public int x; public Issue467TmpBean(int i) { x = i; } } static class Issue709Bean { public byte[] data; } @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="_class") static class LongContainer1940 { public Long longObj; } // [databind#433] static class CustomSerializedPojo implements JsonSerializable { private final ObjectNode node = JsonNodeFactory.instance.objectNode(); public void setFoo(final String foo) { node.put("foo", foo); } @Override public void serialize(final JsonGenerator jgen, final SerializerProvider provider) throws IOException { jgen.writeTree(node); } @Override public void serializeWithType(JsonGenerator g, SerializerProvider provider, TypeSerializer typeSer) throws IOException { WritableTypeId typeIdDef = new WritableTypeId(this, JsonToken.START_OBJECT); typeSer.writeTypePrefix(g, typeIdDef); serialize(g, provider); typeSer.writeTypePrefix(g, typeIdDef); } } /* /********************************************************** /* Unit tests /********************************************************** */ private final ObjectMapper MAPPER = objectMapper(); public void testAsInt() throws Exception { assertEquals(9, IntNode.valueOf(9).asInt()); assertEquals(7, LongNode.valueOf(7L).asInt()); assertEquals(13, new TextNode("13").asInt()); assertEquals(0, new TextNode("foobar").asInt()); assertEquals(27, new TextNode("foobar").asInt(27)); assertEquals(1, BooleanNode.TRUE.asInt()); } public void testAsBoolean() throws Exception { assertEquals(false, BooleanNode.FALSE.asBoolean()); assertEquals(true, BooleanNode.TRUE.asBoolean()); assertEquals(false, IntNode.valueOf(0).asBoolean()); assertEquals(true, IntNode.valueOf(1).asBoolean()); assertEquals(false, LongNode.valueOf(0).asBoolean()); assertEquals(true, LongNode.valueOf(-34L).asBoolean()); assertEquals(true, new TextNode("true").asBoolean()); assertEquals(false, new TextNode("false").asBoolean()); assertEquals(false, new TextNode("barf").asBoolean()); assertEquals(true, new TextNode("barf").asBoolean(true)); assertEquals(true, new POJONode(Boolean.TRUE).asBoolean()); } // Deserializer to trigger the problem described in [JACKSON-554] public static class LeafDeserializer extends JsonDeserializer { @Override public Leaf deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { JsonNode tree = (JsonNode) jp.readValueAsTree(); Leaf leaf = new Leaf(); leaf.value = tree.get("value").intValue(); return leaf; } } public void testTreeToValue() throws Exception { String JSON = "{\"leaf\":{\"value\":13}}"; ObjectMapper mapper = new ObjectMapper(); mapper.addMixIn(Leaf.class, LeafMixIn.class); JsonNode root = mapper.readTree(JSON); // Ok, try converting to bean using two mechanisms Root r1 = mapper.treeToValue(root, Root.class); assertNotNull(r1); assertEquals(13, r1.leaf.value); } // [databind#1208]: should coerce POJOs at least at root level public void testTreeToValueWithPOJO() throws Exception { Calendar c = Calendar.getInstance(); c.setTime(new java.util.Date(0)); ValueNode pojoNode = MAPPER.getNodeFactory().pojoNode(c); Calendar result = MAPPER.treeToValue(pojoNode, Calendar.class); assertNotNull(result); assertEquals(result.getTimeInMillis(), c.getTimeInMillis()); } public void testBase64Text() throws Exception { // let's actually iterate over sets of encoding modes, lengths final int[] LENS = { 1, 2, 3, 4, 7, 9, 32, 33, 34, 35 }; final Base64Variant[] VARIANTS = { Base64Variants.MIME, Base64Variants.MIME_NO_LINEFEEDS, Base64Variants.MODIFIED_FOR_URL, Base64Variants.PEM }; for (int len : LENS) { byte[] input = new byte[len]; for (int i = 0; i < input.length; ++i) { input[i] = (byte) i; } for (Base64Variant variant : VARIANTS) { TextNode n = new TextNode(variant.encode(input)); byte[] data = null; try { data = n.getBinaryValue(variant); } catch (Exception e) { fail("Failed (variant "+variant+", data length "+len+"): "+e.getMessage()); } assertNotNull(data); assertArrayEquals(data, input); // 15-Aug-2018, tatu: [databind#2096] requires another test JsonParser p = new TreeTraversingParser(n); assertEquals(JsonToken.VALUE_STRING, p.nextToken()); try { data = p.getBinaryValue(variant); } catch (Exception e) { fail("Failed (variant "+variant+", data length "+len+"): "+e.getMessage()); } assertNotNull(data); assertArrayEquals(data, input); p.close(); } } } /** * Simple test to verify that byte[] values can be handled properly when * converting, as long as there is metadata (from POJO definitions). */ public void testIssue709() throws Exception { byte[] inputData = new byte[] { 1, 2, 3 }; ObjectNode node = MAPPER.createObjectNode(); node.put("data", inputData); Issue709Bean result = MAPPER.treeToValue(node, Issue709Bean.class); String json = MAPPER.writeValueAsString(node); Issue709Bean resultFromString = MAPPER.readValue(json, Issue709Bean.class); Issue709Bean resultFromConvert = MAPPER.convertValue(node, Issue709Bean.class); // all methods should work equally well: Assert.assertArrayEquals(inputData, resultFromString.data); Assert.assertArrayEquals(inputData, resultFromConvert.data); Assert.assertArrayEquals(inputData, result.data); } public void testEmbeddedByteArray() throws Exception { TokenBuffer buf = new TokenBuffer(MAPPER, false); buf.writeObject(new byte[3]); JsonNode node = MAPPER.readTree(buf.asParser()); buf.close(); assertTrue(node.isBinary()); byte[] data = node.binaryValue(); assertNotNull(data); assertEquals(3, data.length); } // [databind#232] public void testBigDecimalAsPlainStringTreeConversion() throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); Map map = new HashMap(); String PI_STR = "3.00000000"; map.put("pi", new BigDecimal(PI_STR)); JsonNode tree = mapper.valueToTree(map); assertNotNull(tree); assertEquals(1, tree.size()); assertTrue(tree.has("pi")); } // [databind#433] public void testBeanToTree() throws Exception { final CustomSerializedPojo pojo = new CustomSerializedPojo(); pojo.setFoo("bar"); final JsonNode node = MAPPER.valueToTree(pojo); assertEquals(JsonNodeType.OBJECT, node.getNodeType()); } // [databind#467] public void testConversionOfPojos() throws Exception { final Issue467Bean input = new Issue467Bean(13); final String EXP = "{\"x\":13}"; // first, sanity check String json = MAPPER.writeValueAsString(input); assertEquals(EXP, json); // then via conversions: should become JSON Object JsonNode tree = MAPPER.valueToTree(input); assertTrue("Expected Object, got "+tree.getNodeType(), tree.isObject()); assertEquals(EXP, MAPPER.writeValueAsString(tree)); } // [databind#467] public void testConversionOfTrees() throws Exception { final Issue467Tree input = new Issue467Tree(); final String EXP = "true"; // first, sanity check String json = MAPPER.writeValueAsString(input); assertEquals(EXP, json); // then via conversions: should become JSON Object JsonNode tree = MAPPER.valueToTree(input); assertTrue("Expected Object, got "+tree.getNodeType(), tree.isBoolean()); assertEquals(EXP, MAPPER.writeValueAsString(tree)); } // [databind#1940]: losing of precision due to coercion public void testBufferedLongViaCoercion() throws Exception { long EXP = 1519348261000L; JsonNode tree = MAPPER.readTree("{\"longObj\": "+EXP+".0, \"_class\": \""+LongContainer1940.class.getName()+"\"}"); LongContainer1940 obj = MAPPER.treeToValue(tree, LongContainer1940.class); assertEquals(Long.valueOf(EXP), obj.longObj); } public void testConversionsOfNull() throws Exception { // First: `null` value should become `NullNode` JsonNode n = MAPPER.valueToTree(null); assertNotNull(n); assertTrue(n.isNull()); // and vice versa Object pojo = MAPPER.treeToValue(n, Root.class); assertNull(pojo); } }