package com.fasterxml.jackson.databind.deser.jdk;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
public class JDKNumberDeserTest extends BaseMapTest
{
/*
/**********************************************************************
/* Helper classes, beans
/**********************************************************************
*/
static class MyBeanHolder {
public Long id;
public MyBeanDefaultValue defaultValue;
}
static class MyBeanDefaultValue
{
public MyBeanValue value;
}
@JsonDeserialize(using=MyBeanDeserializer.class)
static class MyBeanValue {
public BigDecimal decimal;
public MyBeanValue() { this(null); }
public MyBeanValue(BigDecimal d) { this.decimal = d; }
}
/*
/**********************************************************************
/* Helper classes, serializers/deserializers/resolvers
/**********************************************************************
*/
static class MyBeanDeserializer extends JsonDeserializer<MyBeanValue>
{
@Override
public MyBeanValue deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException
{
return new MyBeanValue(jp.getDecimalValue());
}
}
/*
/**********************************************************************
/* Unit tests
/**********************************************************************
*/
final ObjectMapper MAPPER = new ObjectMapper();
public void testNaN() throws Exception
{
Float result = MAPPER.readValue(" \"NaN\"", Float.class);
assertEquals(Float.valueOf(Float.NaN), result);
Double d = MAPPER.readValue(" \"NaN\"", Double.class);
assertEquals(Double.valueOf(Double.NaN), d);
Number num = MAPPER.readValue(" \"NaN\"", Number.class);
assertEquals(Double.valueOf(Double.NaN), num);
}
public void testDoubleInf() throws Exception
{
Double result = MAPPER.readValue(" \""+Double.POSITIVE_INFINITY+"\"", Double.class);
assertEquals(Double.valueOf(Double.POSITIVE_INFINITY), result);
result = MAPPER.readValue(" \""+Double.NEGATIVE_INFINITY+"\"", Double.class);
assertEquals(Double.valueOf(Double.NEGATIVE_INFINITY), result);
}
// 01-Mar-2017, tatu: This is bit tricky... in some ways, mapping to "empty value"
// would be best; but due to legacy reasons becomes `null` at this point
public void testEmptyAsNumber() throws Exception
{
assertNull(MAPPER.readValue(quote(""), Byte.class));
assertNull(MAPPER.readValue(quote(""), Short.class));
assertNull(MAPPER.readValue(quote(""), Character.class));
assertNull(MAPPER.readValue(quote(""), Integer.class));
assertNull(MAPPER.readValue(quote(""), Long.class));
assertNull(MAPPER.readValue(quote(""), Float.class));
assertNull(MAPPER.readValue(quote(""), Double.class));
assertNull(MAPPER.readValue(quote(""), BigInteger.class));
assertNull(MAPPER.readValue(quote(""), BigDecimal.class));
}
public void testTextualNullAsNumber() throws Exception
{
final String NULL_JSON = quote("null");
assertNull(MAPPER.readValue(NULL_JSON, Byte.class));
assertNull(MAPPER.readValue(NULL_JSON, Short.class));
// Character is bit special, can't do:
// assertNull(MAPPER.readValue(JSON, Character.class));
assertNull(MAPPER.readValue(NULL_JSON, Integer.class));
assertNull(MAPPER.readValue(NULL_JSON, Long.class));
assertNull(MAPPER.readValue(NULL_JSON, Float.class));
assertNull(MAPPER.readValue(NULL_JSON, Double.class));
assertEquals(Byte.valueOf((byte) 0), MAPPER.readValue(NULL_JSON, Byte.TYPE));
assertEquals(Short.valueOf((short) 0), MAPPER.readValue(NULL_JSON, Short.TYPE));
// Character is bit special, can't do:
// assertEquals(Character.valueOf((char) 0), MAPPER.readValue(JSON, Character.TYPE));
assertEquals(Integer.valueOf(0), MAPPER.readValue(NULL_JSON, Integer.TYPE));
assertEquals(Long.valueOf(0L), MAPPER.readValue(NULL_JSON, Long.TYPE));
assertEquals(Float.valueOf(0f), MAPPER.readValue(NULL_JSON, Float.TYPE));
assertEquals(Double.valueOf(0d), MAPPER.readValue(NULL_JSON, Double.TYPE));
assertNull(MAPPER.readValue(NULL_JSON, BigInteger.class));
assertNull(MAPPER.readValue(NULL_JSON, BigDecimal.class));
// Also: verify failure for at least some
try {
MAPPER.readerFor(Integer.TYPE).with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
.readValue(NULL_JSON);
fail("Should not have passed");
} catch (MismatchedInputException e) {
verifyException(e, "Can not coerce String \"null\"");
}
ObjectMapper noCoerceMapper = new ObjectMapper();
noCoerceMapper.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS);
try {
noCoerceMapper.readValue(NULL_JSON, Integer.TYPE);
fail("Should not have passed");
} catch (MismatchedInputException e) {
verifyException(e, "Can not coerce String \"null\"");
}
}
public void testDeserializeDecimalHappyPath() throws Exception {
String json = "{\"defaultValue\": { \"value\": 123 } }";
MyBeanHolder result = MAPPER.readValue(json, MyBeanHolder.class);
assertEquals(BigDecimal.valueOf(123), result.defaultValue.value.decimal);
}
public void testDeserializeDecimalProperException() throws Exception {
String json = "{\"defaultValue\": { \"value\": \"123\" } }";
try {
MAPPER.readValue(json, MyBeanHolder.class);
fail("should have raised exception");
} catch (JsonProcessingException e) {
verifyException(e, "not numeric");
}
}
public void testDeserializeDecimalProperExceptionWhenIdSet() throws Exception {
String json = "{\"id\": 5, \"defaultValue\": { \"value\": \"123\" } }";
try {
MyBeanHolder result = MAPPER.readValue(json, MyBeanHolder.class);
fail("should have raised exception instead value was set to " + result.defaultValue.value.decimal.toString());
} catch (JsonProcessingException e) {
verifyException(e, "not numeric");
}
}
// And then [databind#852]
public void testScientificNotationAsStringForNumber() throws Exception
{
Object ob = MAPPER.readValue("\"3E-8\"", Number.class);
assertEquals(Double.class, ob.getClass());
ob = MAPPER.readValue("\"3e-8\"", Number.class);
assertEquals(Double.class, ob.getClass());
ob = MAPPER.readValue("\"300000000\"", Number.class);
assertEquals(Integer.class, ob.getClass());
ob = MAPPER.readValue("\"123456789012\"", Number.class);
assertEquals(Long.class, ob.getClass());
}
public void testIntAsNumber() throws Exception
{
/* Even if declared as 'generic' type, should return using most
* efficient type... here, Integer
*/
Number result = MAPPER.readValue(" 123 ", Number.class);
assertEquals(Integer.valueOf(123), result);
}
public void testLongAsNumber() throws Exception
{
// And beyond int range, should get long
long exp = 1234567890123L;
Number result = MAPPER.readValue(String.valueOf(exp), Number.class);
assertEquals(Long.valueOf(exp), result);
}
public void testBigIntAsNumber() throws Exception
{
// and after long, BigInteger
BigInteger biggie = new BigInteger("1234567890123456789012345678901234567890");
Number result = MAPPER.readValue(biggie.toString(), Number.class);
assertEquals(BigInteger.class, biggie.getClass());
assertEquals(biggie, result);
}
public void testIntTypeOverride() throws Exception
{
/* Slight twist; as per [JACKSON-100], can also request binding
* to BigInteger even if value would fit in Integer
*/
ObjectReader r = MAPPER.reader(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS);
BigInteger exp = BigInteger.valueOf(123L);
// first test as any Number
Number result = r.forType(Number.class).readValue(" 123 ");
assertEquals(BigInteger.class, result.getClass());
assertEquals(exp, result);
// then as any Object
/*Object value =*/ r.forType(Object.class).readValue("123");
assertEquals(BigInteger.class, result.getClass());
assertEquals(exp, result);
// and as JsonNode
JsonNode node = r.readTree(" 123");
assertTrue(node.isBigInteger());
assertEquals(123, node.asInt());
}
public void testDoubleAsNumber() throws Exception
{
Number result = MAPPER.readValue(new StringReader(" 1.0 "), Number.class);
assertEquals(Double.valueOf(1.0), result);
}
public void testFpTypeOverrideSimple() throws Exception
{
ObjectReader r = MAPPER.reader(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
BigDecimal dec = new BigDecimal("0.1");
// First test generic stand-alone Number
Number result = r.forType(Number.class).readValue(dec.toString());
assertEquals(BigDecimal.class, result.getClass());
assertEquals(dec, result);
// Then plain old Object
Object value = r.forType(Object.class).readValue(dec.toString());
assertEquals(BigDecimal.class, result.getClass());
assertEquals(dec, value);
JsonNode node = r.readTree(dec.toString());
assertTrue(node.isBigDecimal());
assertEquals(dec.doubleValue(), node.asDouble());
}
public void testFpTypeOverrideStructured() throws Exception
{
ObjectReader r = MAPPER.reader(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
BigDecimal dec = new BigDecimal("-19.37");
// List element types
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) r.forType(List.class).readValue("[ "+dec.toString()+" ]");
assertEquals(1, list.size());
Object val = list.get(0);
assertEquals(BigDecimal.class, val.getClass());
assertEquals(dec, val);
// and a map
Map<?,?> map = r.forType(Map.class).readValue("{ \"a\" : "+dec.toString()+" }");
assertEquals(1, map.size());
val = map.get("a");
assertEquals(BigDecimal.class, val.getClass());
assertEquals(dec, val);
}
// [databind#504]
public void testForceIntsToLongs() throws Exception
{
ObjectReader r = MAPPER.reader(DeserializationFeature.USE_LONG_FOR_INTS);
Object ob = r.forType(Object.class).readValue("42");
assertEquals(Long.class, ob.getClass());
assertEquals(Long.valueOf(42L), ob);
Number n = r.forType(Number.class).readValue("42");
assertEquals(Long.class, n.getClass());
assertEquals(Long.valueOf(42L), n);
// and one more: should get proper node as well
JsonNode node = r.readTree("42");
if (!node.isLong()) {
fail("Expected LongNode, got: "+node.getClass().getName());
}
assertEquals(42, node.asInt());
}
}