package com.fasterxml.jackson.databind.node;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.*;
/**
* Basic tests for {@link JsonNode} implementations that
* contain numeric values.
*/
public class NumberNodesTest extends NodeTestBase
{
private final ObjectMapper MAPPER = objectMapper();
public void testShort()
{
ShortNode n = ShortNode.valueOf((short) 1);
assertStandardEquals(n);
assertTrue(0 != n.hashCode());
assertEquals(JsonToken.VALUE_NUMBER_INT, n.asToken());
assertEquals(JsonParser.NumberType.INT, n.numberType()); // should be SHORT
assertEquals(1, n.intValue());
assertEquals(1L, n.longValue());
assertEquals(BigDecimal.ONE, n.decimalValue());
assertEquals(BigInteger.ONE, n.bigIntegerValue());
assertEquals("1", n.asText());
assertNodeNumbers(n, 1, 1.0);
assertTrue(ShortNode.valueOf((short) 0).canConvertToInt());
assertTrue(ShortNode.valueOf(Short.MAX_VALUE).canConvertToInt());
assertTrue(ShortNode.valueOf(Short.MIN_VALUE).canConvertToInt());
assertTrue(ShortNode.valueOf((short) 0).canConvertToLong());
assertTrue(ShortNode.valueOf(Short.MAX_VALUE).canConvertToLong());
assertTrue(ShortNode.valueOf(Short.MIN_VALUE).canConvertToLong());
}
public void testIntViaMapper() throws Exception
{
int value = -90184;
JsonNode result = MAPPER.readTree(String.valueOf(value));
assertTrue(result.isNumber());
assertTrue(result.isIntegralNumber());
assertTrue(result.isInt());
assertType(result, IntNode.class);
assertFalse(result.isLong());
assertFalse(result.isFloatingPointNumber());
assertFalse(result.isDouble());
assertFalse(result.isNull());
assertFalse(result.isTextual());
assertFalse(result.isMissingNode());
assertEquals(value, result.numberValue().intValue());
assertEquals(value, result.intValue());
assertEquals(String.valueOf(value), result.asText());
assertEquals((double) value, result.doubleValue());
assertEquals((long) value, result.longValue());
// also, equality should work ok
assertEquals(result, IntNode.valueOf(value));
}
public void testInt()
{
IntNode n = IntNode.valueOf(1);
assertStandardEquals(n);
assertTrue(0 != n.hashCode());
assertEquals(JsonToken.VALUE_NUMBER_INT, n.asToken());
assertEquals(JsonParser.NumberType.INT, n.numberType());
assertEquals(1, n.intValue());
assertEquals(1L, n.longValue());
assertEquals(BigDecimal.ONE, n.decimalValue());
assertEquals(BigInteger.ONE, n.bigIntegerValue());
assertEquals("1", n.asText());
// 2.4
assertEquals("1", n.asText("foo"));
assertNodeNumbers(n, 1, 1.0);
assertTrue(IntNode.valueOf(0).canConvertToInt());
assertTrue(IntNode.valueOf(Integer.MAX_VALUE).canConvertToInt());
assertTrue(IntNode.valueOf(Integer.MIN_VALUE).canConvertToInt());
assertTrue(IntNode.valueOf(0).canConvertToLong());
assertTrue(IntNode.valueOf(Integer.MAX_VALUE).canConvertToLong());
assertTrue(IntNode.valueOf(Integer.MIN_VALUE).canConvertToLong());
}
public void testLong()
{
LongNode n = LongNode.valueOf(1L);
assertStandardEquals(n);
assertTrue(0 != n.hashCode());
assertEquals(JsonToken.VALUE_NUMBER_INT, n.asToken());
assertEquals(JsonParser.NumberType.LONG, n.numberType());
assertEquals(1, n.intValue());
assertEquals(1L, n.longValue());
assertEquals(BigDecimal.ONE, n.decimalValue());
assertEquals(BigInteger.ONE, n.bigIntegerValue());
assertEquals("1", n.asText());
assertNodeNumbers(n, 1, 1.0);
// ok if contains small enough value
assertTrue(LongNode.valueOf(0).canConvertToInt());
assertTrue(LongNode.valueOf(Integer.MAX_VALUE).canConvertToInt());
assertTrue(LongNode.valueOf(Integer.MIN_VALUE).canConvertToInt());
// but not in other cases
assertFalse(LongNode.valueOf(1L + Integer.MAX_VALUE).canConvertToInt());
assertFalse(LongNode.valueOf(-1L + Integer.MIN_VALUE).canConvertToInt());
assertTrue(LongNode.valueOf(0L).canConvertToLong());
assertTrue(LongNode.valueOf(Long.MAX_VALUE).canConvertToLong());
assertTrue(LongNode.valueOf(Long.MIN_VALUE).canConvertToLong());
}
public void testLongViaMapper() throws Exception
{
// need to use something being 32-bit value space
long value = 12345678L << 32;
JsonNode result = MAPPER.readTree(String.valueOf(value));
assertTrue(result.isNumber());
assertTrue(result.isIntegralNumber());
assertTrue(result.isLong());
assertType(result, LongNode.class);
assertFalse(result.isInt());
assertFalse(result.isFloatingPointNumber());
assertFalse(result.isDouble());
assertFalse(result.isNull());
assertFalse(result.isTextual());
assertFalse(result.isMissingNode());
assertEquals(value, result.numberValue().longValue());
assertEquals(value, result.longValue());
assertEquals(String.valueOf(value), result.asText());
assertEquals((double) value, result.doubleValue());
// also, equality should work ok
assertEquals(result, LongNode.valueOf(value));
}
public void testDouble() throws Exception
{
DoubleNode n = DoubleNode.valueOf(0.25);
assertStandardEquals(n);
assertTrue(0 != n.hashCode());
assertEquals(JsonToken.VALUE_NUMBER_FLOAT, n.asToken());
assertEquals(JsonParser.NumberType.DOUBLE, n.numberType());
assertEquals(0, n.intValue());
assertEquals(0.25, n.doubleValue());
assertNotNull(n.decimalValue());
assertEquals(BigInteger.ZERO, n.bigIntegerValue());
assertEquals("0.25", n.asText());
assertNodeNumbers(DoubleNode.valueOf(4.5), 4, 4.5);
assertTrue(DoubleNode.valueOf(0).canConvertToInt());
assertTrue(DoubleNode.valueOf(Integer.MAX_VALUE).canConvertToInt());
assertTrue(DoubleNode.valueOf(Integer.MIN_VALUE).canConvertToInt());
assertFalse(DoubleNode.valueOf(1L + Integer.MAX_VALUE).canConvertToInt());
assertFalse(DoubleNode.valueOf(-1L + Integer.MIN_VALUE).canConvertToInt());
assertTrue(DoubleNode.valueOf(0L).canConvertToLong());
assertTrue(DoubleNode.valueOf(Long.MAX_VALUE).canConvertToLong());
assertTrue(DoubleNode.valueOf(Long.MIN_VALUE).canConvertToLong());
JsonNode num = objectMapper().readTree(" -0.0");
assertTrue(num.isDouble());
n = (DoubleNode) num;
assertEquals(-0.0, n.doubleValue());
assertEquals("-0.0", String.valueOf(n.doubleValue()));
}
public void testDoubleViaMapper() throws Exception
{
double value = 3.04;
JsonNode result = MAPPER.readTree(String.valueOf(value));
assertTrue(result.isNumber());
assertFalse(result.isNull());
assertType(result, DoubleNode.class);
assertTrue(result.isFloatingPointNumber());
assertTrue(result.isDouble());
assertFalse(result.isInt());
assertFalse(result.isLong());
assertFalse(result.isIntegralNumber());
assertFalse(result.isTextual());
assertFalse(result.isMissingNode());
assertEquals(value, result.doubleValue());
assertEquals(value, result.numberValue().doubleValue());
assertEquals((int) value, result.intValue());
assertEquals((long) value, result.longValue());
assertEquals(String.valueOf(value), result.asText());
// also, equality should work ok
assertEquals(result, DoubleNode.valueOf(value));
}
// @since 2.2
public void testFloat()
{
FloatNode n = FloatNode.valueOf(0.45f);
assertStandardEquals(n);
assertTrue(0 != n.hashCode());
assertEquals(JsonToken.VALUE_NUMBER_FLOAT, n.asToken());
assertEquals(JsonParser.NumberType.FLOAT, n.numberType());
assertEquals(0, n.intValue());
// NOTE: conversion to double NOT as simple as with exact numbers like 0.25:
assertEquals(0.45f, n.floatValue());
assertEquals("0.45", n.asText());
// so; as double we'll get more complex number; however, should round-trip
// to something that gets printed the same way. But not exact value, alas, hence:
assertEquals("0.45", String.valueOf((float) n.doubleValue()));
assertNotNull(n.decimalValue());
// possibly surprisingly, however, this will produce same output:
assertEquals(BigInteger.ZERO, n.bigIntegerValue());
assertEquals("0.45", n.asText());
// 1.6:
assertNodeNumbers(FloatNode.valueOf(4.5f), 4, 4.5f);
assertTrue(FloatNode.valueOf(0).canConvertToInt());
assertTrue(FloatNode.valueOf(Integer.MAX_VALUE).canConvertToInt());
assertTrue(FloatNode.valueOf(Integer.MIN_VALUE).canConvertToInt());
// rounding errors if we just add/sub 1... so:
assertFalse(FloatNode.valueOf(1000L + Integer.MAX_VALUE).canConvertToInt());
assertFalse(FloatNode.valueOf(-1000L + Integer.MIN_VALUE).canConvertToInt());
assertTrue(FloatNode.valueOf(0L).canConvertToLong());
assertTrue(FloatNode.valueOf(Integer.MAX_VALUE).canConvertToLong());
assertTrue(FloatNode.valueOf(Integer.MIN_VALUE).canConvertToLong());
}
public void testDecimalNode() throws Exception
{
DecimalNode n = DecimalNode.valueOf(BigDecimal.ONE);
assertStandardEquals(n);
assertTrue(n.equals(new DecimalNode(BigDecimal.ONE)));
assertEquals(JsonToken.VALUE_NUMBER_FLOAT, n.asToken());
assertEquals(JsonParser.NumberType.BIG_DECIMAL, n.numberType());
assertTrue(n.isNumber());
assertFalse(n.isIntegralNumber());
assertFalse(n.isArray());
assertTrue(n.isBigDecimal());
assertEquals(BigDecimal.ONE, n.numberValue());
assertEquals(1, n.intValue());
assertEquals(1L, n.longValue());
assertEquals(BigDecimal.ONE, n.decimalValue());
assertEquals("1", n.asText());
assertNodeNumbers(n, 1, 1.0);
assertTrue(DecimalNode.valueOf(BigDecimal.ZERO).canConvertToInt());
assertTrue(DecimalNode.valueOf(BigDecimal.valueOf(Integer.MAX_VALUE)).canConvertToInt());
assertTrue(DecimalNode.valueOf(BigDecimal.valueOf(Integer.MIN_VALUE)).canConvertToInt());
assertFalse(DecimalNode.valueOf(BigDecimal.valueOf(1L + Integer.MAX_VALUE)).canConvertToInt());
assertFalse(DecimalNode.valueOf(BigDecimal.valueOf(-1L + Integer.MIN_VALUE)).canConvertToInt());
assertTrue(DecimalNode.valueOf(BigDecimal.ZERO).canConvertToLong());
assertTrue(DecimalNode.valueOf(BigDecimal.valueOf(Long.MAX_VALUE)).canConvertToLong());
assertTrue(DecimalNode.valueOf(BigDecimal.valueOf(Long.MIN_VALUE)).canConvertToLong());
// no "natural" way to get it, must construct
BigDecimal value = new BigDecimal("0.1");
JsonNode result = DecimalNode.valueOf(value);
assertFalse(result.isObject());
assertTrue(result.isNumber());
assertFalse(result.isIntegralNumber());
assertFalse(result.isLong());
assertType(result, DecimalNode.class);
assertFalse(result.isInt());
assertTrue(result.isFloatingPointNumber());
assertTrue(result.isBigDecimal());
assertFalse(result.isDouble());
assertFalse(result.isNull());
assertFalse(result.isTextual());
assertFalse(result.isMissingNode());
assertEquals(value, result.numberValue());
assertEquals(value.toString(), result.asText());
// also, equality should work ok
assertEquals(result, DecimalNode.valueOf(value));
}
public void testDecimalNodeEqualsHashCode()
{
/*
* We want DecimalNodes with equivalent _numeric_ values to be equal;
* this is not the case for BigDecimal where "1.0" and "1" are not
* equal!
*/
BigDecimal b1 = BigDecimal.ONE;
BigDecimal b2 = new BigDecimal("1.0");
BigDecimal b3 = new BigDecimal("0.01e2");
BigDecimal b4 = new BigDecimal("1000e-3");
DecimalNode node1 = new DecimalNode(b1);
DecimalNode node2 = new DecimalNode(b2);
DecimalNode node3 = new DecimalNode(b3);
DecimalNode node4 = new DecimalNode(b4);
assertEquals(node1.hashCode(), node2.hashCode());
assertEquals(node2.hashCode(), node3.hashCode());
assertEquals(node3.hashCode(), node4.hashCode());
assertEquals(node1, node2);
assertEquals(node2, node1);
assertEquals(node2, node3);
assertEquals(node3, node4);
}
public void testBigIntegerNode() throws Exception
{
BigIntegerNode n = BigIntegerNode.valueOf(BigInteger.ONE);
assertStandardEquals(n);
assertTrue(n.equals(new BigIntegerNode(BigInteger.ONE)));
assertEquals(JsonToken.VALUE_NUMBER_INT, n.asToken());
assertEquals(JsonParser.NumberType.BIG_INTEGER, n.numberType());
assertTrue(n.isNumber());
assertTrue(n.isIntegralNumber());
assertTrue(n.isBigInteger());
assertEquals(BigInteger.ONE, n.numberValue());
assertEquals(1, n.intValue());
assertEquals(1L, n.longValue());
assertEquals(BigInteger.ONE, n.bigIntegerValue());
assertEquals("1", n.asText());
// 1.6:
assertNodeNumbers(n, 1, 1.0);
BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE);
n = BigIntegerNode.valueOf(maxLong);
assertEquals(Long.MAX_VALUE, n.longValue());
ObjectMapper mapper = new ObjectMapper();
JsonNode n2 = mapper.readTree(maxLong.toString());
assertEquals(Long.MAX_VALUE, n2.longValue());
// then over long limit:
BigInteger beyondLong = maxLong.shiftLeft(2); // 4x max long
n2 = mapper.readTree(beyondLong.toString());
assertEquals(beyondLong, n2.bigIntegerValue());
assertTrue(BigIntegerNode.valueOf(BigInteger.ZERO).canConvertToInt());
assertTrue(BigIntegerNode.valueOf(BigInteger.valueOf(Integer.MAX_VALUE)).canConvertToInt());
assertTrue(BigIntegerNode.valueOf(BigInteger.valueOf(Integer.MIN_VALUE)).canConvertToInt());
assertFalse(BigIntegerNode.valueOf(BigInteger.valueOf(1L + Integer.MAX_VALUE)).canConvertToInt());
assertFalse(BigIntegerNode.valueOf(BigInteger.valueOf(-1L + Integer.MIN_VALUE)).canConvertToInt());
assertTrue(BigIntegerNode.valueOf(BigInteger.ZERO).canConvertToLong());
assertTrue(BigIntegerNode.valueOf(BigInteger.valueOf(Long.MAX_VALUE)).canConvertToLong());
assertTrue(BigIntegerNode.valueOf(BigInteger.valueOf(Long.MIN_VALUE)).canConvertToLong());
}
public void testBigDecimalAsPlain() throws Exception
{
ObjectMapper mapper = new ObjectMapper()
.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
final String INPUT = "{\"x\":1e2}";
final JsonNode node = mapper.readTree(INPUT);
String result = mapper.writeValueAsString(node);
assertEquals("{\"x\":100}", result);
// also via ObjectWriter:
assertEquals("{\"x\":100}", mapper.writer().writeValueAsString(node));
// and once more for [core#175]:
BigDecimal bigDecimal = new BigDecimal(100);
JsonNode tree = mapper.valueToTree(bigDecimal);
assertEquals("100", mapper.writeValueAsString(tree));
}
public void testCanonicalNumbers() throws Exception
{
JsonNodeFactory f = new JsonNodeFactory();
NumericNode n = f.numberNode(123);
assertTrue(n.isInt());
n = f.numberNode(1L + Integer.MAX_VALUE);
assertFalse(n.isInt());
assertTrue(n.isLong());
/* 19-May-2015, tatu: Actually, no, coercion should not happen by default.
* But it should be possible to change it if necessary.
*/
// but "too small" number will be 'int'...
n = f.numberNode(123L);
assertTrue(n.isLong());
}
}