• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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