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.core.*;
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<Issue467Bean> {
@Override
public void serialize(Issue467Bean value, JsonGenerator jgen,
SerializerProvider provider) throws IOException {
jgen.writeObject(new Issue467TmpBean(value.i));
}
}
static class Issue467TreeSerializer extends JsonSerializer<Issue467Tree> {
@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; }
}
/*
/**********************************************************
/* 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<Leaf>
{
@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) {
throw new IOException("Failed (variant "+variant+", data length "+len+"): "+e.getMessage());
}
assertNotNull(data);
assertArrayEquals(data, input);
}
}
}
static class Issue709Bean {
public byte[] data;
}
/**
* 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<String, Object> map = new HashMap<String, Object>();
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"));
}
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 jgen,
SerializerProvider provider, TypeSerializer typeSer) throws IOException {
typeSer.writeTypePrefixForObject(this, jgen);
serialize(jgen, provider);
typeSer.writeTypeSuffixForObject(this, jgen);
}
}
// [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));
}
}