blob: 722bfe4b0fd0fb12c911358cececfc24f1f4dc3e [file] [log] [blame]
package com.fasterxml.jackson.databind.node;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import static org.junit.Assert.*;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.JsonParser.NumberType;
import com.fasterxml.jackson.core.exc.InputCoercionException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
public class TestTreeTraversingParser
extends BaseMapTest
{
static class Person {
public String name;
public int magicNumber;
public List<String> kids;
}
@JsonIgnoreProperties(ignoreUnknown=true)
public static class Jackson370Bean {
public Inner inner;
}
public static class Inner {
public String value;
}
/*
/**********************************************************
/* Test methods
/**********************************************************
*/
private final ObjectMapper MAPPER = newJsonMapper();
public void testSimple() throws Exception
{
// For convenience, parse tree from JSON first
final String JSON =
"{ \"a\" : 123, \"list\" : [ 12.25, null, true, { }, [ ] ] }";
JsonNode tree = MAPPER.readTree(JSON);
JsonParser p = tree.traverse();
assertNull(p.getCurrentToken());
assertNull(p.getCurrentName());
assertToken(JsonToken.START_OBJECT, p.nextToken());
assertNull(p.getCurrentName());
assertEquals("Expected START_OBJECT", JsonToken.START_OBJECT.asString(), p.getText());
assertToken(JsonToken.FIELD_NAME, p.nextToken());
assertEquals("a", p.getCurrentName());
assertEquals("a", p.getText());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals("a", p.getCurrentName());
assertEquals(123, p.getIntValue());
assertEquals("123", p.getText());
assertToken(JsonToken.FIELD_NAME, p.nextToken());
assertEquals("list", p.getCurrentName());
assertEquals("list", p.getText());
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertEquals("list", p.getCurrentName());
assertEquals(JsonToken.START_ARRAY.asString(), p.getText());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertNull(p.getCurrentName());
assertEquals(12.25, p.getDoubleValue(), 0);
assertEquals("12.25", p.getText());
assertToken(JsonToken.VALUE_NULL, p.nextToken());
assertNull(p.getCurrentName());
assertEquals(JsonToken.VALUE_NULL.asString(), p.getText());
assertToken(JsonToken.VALUE_TRUE, p.nextToken());
assertNull(p.getCurrentName());
assertTrue(p.getBooleanValue());
assertEquals(JsonToken.VALUE_TRUE.asString(), p.getText());
assertToken(JsonToken.START_OBJECT, p.nextToken());
assertNull(p.getCurrentName());
assertToken(JsonToken.END_OBJECT, p.nextToken());
assertNull(p.getCurrentName());
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertNull(p.getCurrentName());
assertToken(JsonToken.END_ARRAY, p.nextToken());
assertNull(p.getCurrentName());
assertToken(JsonToken.END_ARRAY, p.nextToken());
assertToken(JsonToken.END_OBJECT, p.nextToken());
assertNull(p.getCurrentName());
assertNull(p.nextToken());
p.close();
assertTrue(p.isClosed());
}
public void testArray() throws Exception
{
// For convenience, parse tree from JSON first
JsonParser p = MAPPER.readTree("[]").traverse();
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
p.close();
p = MAPPER.readTree("[[]]").traverse();
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
p.close();
p = MAPPER.readTree("[[ 12.1 ]]").traverse();
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
p.close();
}
public void testNested() throws Exception
{
// For convenience, parse tree from JSON first
final String JSON =
"{\"coordinates\":[[[-3,\n1],[179.859681,51.175092]]]}"
;
JsonNode tree = MAPPER.readTree(JSON);
JsonParser p = tree.traverse();
assertToken(JsonToken.START_OBJECT, p.nextToken());
assertToken(JsonToken.FIELD_NAME, p.nextToken());
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
assertToken(JsonToken.END_OBJECT, p.nextToken());
p.close();
}
/**
* Unit test that verifies that we can (re)parse sample document
* from JSON specification.
*/
public void testSpecDoc() throws Exception
{
JsonNode tree = MAPPER.readTree(SAMPLE_DOC_JSON_SPEC);
JsonParser p = tree.traverse();
verifyJsonSpecSampleDoc(p, true);
p.close();
}
public void testBinaryPojo() throws Exception
{
byte[] inputBinary = new byte[] { 1, 2, 100 };
POJONode n = new POJONode(inputBinary);
JsonParser p = n.traverse();
assertNull(p.getCurrentToken());
assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
byte[] data = p.getBinaryValue();
assertNotNull(data);
assertArrayEquals(inputBinary, data);
Object pojo = p.getEmbeddedObject();
assertSame(data, pojo);
p.close();
}
public void testBinaryNode() throws Exception
{
byte[] inputBinary = new byte[] { 0, -5 };
BinaryNode n = new BinaryNode(inputBinary);
JsonParser p = n.traverse();
assertNull(p.getCurrentToken());
// exposed as POJO... not as VALUE_STRING
assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
byte[] data = p.getBinaryValue();
assertNotNull(data);
assertArrayEquals(inputBinary, data);
// but as importantly, can be viewed as base64 encoded thing:
assertEquals("APs=", p.getText());
assertNull(p.nextToken());
p.close();
}
public void testTextAsBinary() throws Exception
{
TextNode n = new TextNode(" APs=\n");
JsonParser p = n.traverse();
assertNull(p.getCurrentToken());
assertToken(JsonToken.VALUE_STRING, p.nextToken());
byte[] data = p.getBinaryValue();
assertNotNull(data);
assertArrayEquals(new byte[] { 0, -5 }, data);
assertNull(p.nextToken());
p.close();
assertTrue(p.isClosed());
// Also: let's verify we get an exception for garbage...
n = new TextNode("?!??");
p = n.traverse();
assertToken(JsonToken.VALUE_STRING, p.nextToken());
try {
p.getBinaryValue();
} catch (InvalidFormatException e) {
verifyException(e, "Illegal character");
}
p.close();
}
/**
* Very simple test case to verify that tree-to-POJO
* conversion works ok
*/
public void testDataBind() throws Exception
{
JsonNode tree = MAPPER.readTree
("{ \"name\" : \"Tatu\", \n"
+"\"magicNumber\" : 42,"
+"\"kids\" : [ \"Leo\", \"Lila\", \"Leia\" ] \n"
+"}");
Person tatu = MAPPER.treeToValue(tree, Person.class);
assertNotNull(tatu);
assertEquals(42, tatu.magicNumber);
assertEquals("Tatu", tatu.name);
assertNotNull(tatu.kids);
assertEquals(3, tatu.kids.size());
assertEquals("Leo", tatu.kids.get(0));
assertEquals("Lila", tatu.kids.get(1));
assertEquals("Leia", tatu.kids.get(2));
}
public void testSkipChildrenWrt370() throws Exception
{
ObjectNode n = MAPPER.createObjectNode();
n.putObject("inner").put("value", "test");
n.putObject("unknown").putNull("inner");
Jackson370Bean obj = MAPPER.readValue(n.traverse(), Jackson370Bean.class);
assertNotNull(obj.inner);
assertEquals("test", obj.inner.value);
}
// // // Numeric coercion checks, [databind#2189]
public void testNumberOverflowInt() throws IOException
{
final long tooBig = 1L + Integer.MAX_VALUE;
try (final JsonParser p = MAPPER.readTree("[ "+tooBig+" ]").traverse()) {
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(NumberType.LONG, p.getNumberType());
try {
p.getIntValue();
fail("Expected failure for `int` overflow");
} catch (InputCoercionException e) {
verifyException(e, "Numeric value ("+tooBig+") out of range of int");
}
}
try (final JsonParser p = MAPPER.readTree("{ \"value\" : "+tooBig+" }").traverse()) {
assertToken(JsonToken.START_OBJECT, p.nextToken());
assertToken(JsonToken.FIELD_NAME, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(NumberType.LONG, p.getNumberType());
try {
p.getIntValue();
fail("Expected failure for `int` overflow");
} catch (InputCoercionException e) {
verifyException(e, "Numeric value ("+tooBig+") out of range of int");
}
}
// But also from floating-point
final String tooBig2 = "1.0e10";
try (final JsonParser p = MAPPER.readTree("[ "+tooBig2+" ]").traverse()) {
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertEquals(NumberType.DOUBLE, p.getNumberType());
try {
p.getIntValue();
fail("Expected failure for `int` overflow");
} catch (InputCoercionException e) {
verifyException(e, "Numeric value ("+tooBig2+") out of range of int");
}
}
}
public void testNumberOverflowLong() throws IOException
{
final BigInteger tooBig = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
try (final JsonParser p = MAPPER.readTree("[ "+tooBig+" ]").traverse()) {
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(NumberType.BIG_INTEGER, p.getNumberType());
try {
p.getLongValue();
fail("Expected failure for `long` overflow");
} catch (InputCoercionException e) {
verifyException(e, "Numeric value ("+tooBig+") out of range of long");
}
}
try (final JsonParser p = MAPPER.readTree("{ \"value\" : "+tooBig+" }").traverse()) {
assertToken(JsonToken.START_OBJECT, p.nextToken());
assertToken(JsonToken.FIELD_NAME, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(NumberType.BIG_INTEGER, p.getNumberType());
try {
p.getLongValue();
fail("Expected failure for `long` overflow");
} catch (InputCoercionException e) {
verifyException(e, "Numeric value ("+tooBig+") out of range of long");
}
}
// But also from floating-point
final String tooBig2 = "1.0e30";
try (final JsonParser p = MAPPER.readTree("[ "+tooBig2+" ]").traverse()) {
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertEquals(NumberType.DOUBLE, p.getNumberType());
try {
p.getLongValue();
fail("Expected failure for `long` overflow");
} catch (InputCoercionException e) {
verifyException(e, "Numeric value ("+tooBig2+") out of range of long");
}
}
// Plus, wrt [databind#2393], NON-failing cases
final long[] okValues = new long[] { 1L+Integer.MAX_VALUE, -1L + Integer.MIN_VALUE,
Long.MAX_VALUE, Long.MIN_VALUE };
for (long okValue : okValues) {
try (final JsonParser p = MAPPER.readTree("{ \"value\" : "+okValue+" }").traverse()) {
assertToken(JsonToken.START_OBJECT, p.nextToken());
assertToken(JsonToken.FIELD_NAME, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(NumberType.LONG, p.getNumberType());
assertEquals(okValue, p.getLongValue());
assertToken(JsonToken.END_OBJECT, p.nextToken());
}
}
}
}