package com.fasterxml.jackson.databind.deser.std;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashSet;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.io.NumberInput;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.util.AccessPattern;
/**
* Container class for deserializers that handle core JDK primitive
* (and matching wrapper) types, as well as standard "big" numeric types.
* Note that this includes types such as {@link java.lang.Boolean}
* and {@link java.lang.Character} which are not strictly numeric,
* but are part of primitive/wrapper types.
*/
public class NumberDeserializers
{
private final static HashSet<String> _classNames = new HashSet<String>();
static {
// note: can skip primitive types; other ways to check them:
Class<?>[] numberTypes = new Class<?>[] {
Boolean.class,
Byte.class,
Short.class,
Character.class,
Integer.class,
Long.class,
Float.class,
Double.class,
// and more generic ones
Number.class, BigDecimal.class, BigInteger.class
};
for (Class<?> cls : numberTypes) {
_classNames.add(cls.getName());
}
}
public static JsonDeserializer<?> find(Class<?> rawType, String clsName) {
if (rawType.isPrimitive()) {
if (rawType == Integer.TYPE) {
return IntegerDeserializer.primitiveInstance;
}
if (rawType == Boolean.TYPE) {
return BooleanDeserializer.primitiveInstance;
}
if (rawType == Long.TYPE) {
return LongDeserializer.primitiveInstance;
}
if (rawType == Double.TYPE) {
return DoubleDeserializer.primitiveInstance;
}
if (rawType == Character.TYPE) {
return CharacterDeserializer.primitiveInstance;
}
if (rawType == Byte.TYPE) {
return ByteDeserializer.primitiveInstance;
}
if (rawType == Short.TYPE) {
return ShortDeserializer.primitiveInstance;
}
if (rawType == Float.TYPE) {
return FloatDeserializer.primitiveInstance;
}
} else if (_classNames.contains(clsName)) {
// Start with most common types; int, boolean, long, double
if (rawType == Integer.class) {
return IntegerDeserializer.wrapperInstance;
}
if (rawType == Boolean.class) {
return BooleanDeserializer.wrapperInstance;
}
if (rawType == Long.class) {
return LongDeserializer.wrapperInstance;
}
if (rawType == Double.class) {
return DoubleDeserializer.wrapperInstance;
}
if (rawType == Character.class) {
return CharacterDeserializer.wrapperInstance;
}
if (rawType == Byte.class) {
return ByteDeserializer.wrapperInstance;
}
if (rawType == Short.class) {
return ShortDeserializer.wrapperInstance;
}
if (rawType == Float.class) {
return FloatDeserializer.wrapperInstance;
}
if (rawType == Number.class) {
return NumberDeserializer.instance;
}
if (rawType == BigDecimal.class) {
return BigDecimalDeserializer.instance;
}
if (rawType == BigInteger.class) {
return BigIntegerDeserializer.instance;
}
} else {
return null;
}
// should never occur
throw new IllegalArgumentException("Internal error: can't find deserializer for "+rawType.getName());
}
/*
/**********************************************************
/* Then one intermediate base class for things that have
/* both primitive and wrapper types
/**********************************************************
*/
protected abstract static class PrimitiveOrWrapperDeserializer<T>
extends StdScalarDeserializer<T>
{
private static final long serialVersionUID = 1L;
protected final T _nullValue;
// @since 2.9
protected final T _emptyValue;
protected final boolean _primitive;
protected PrimitiveOrWrapperDeserializer(Class<T> vc, T nvl, T empty) {
super(vc);
_nullValue = nvl;
_emptyValue = empty;
_primitive = vc.isPrimitive();
}
@Override
public AccessPattern getNullAccessPattern() {
// 02-Feb-2017, tatu: For primitives we must dynamically check (and possibly throw
// exception); for wrappers not.
if (_primitive) {
return AccessPattern.DYNAMIC;
}
if (_nullValue == null) {
return AccessPattern.ALWAYS_NULL;
}
return AccessPattern.CONSTANT;
}
@Override
public final T getNullValue(DeserializationContext ctxt) throws JsonMappingException {
// 01-Mar-2017, tatu: Alas, not all paths lead to `_coerceNull()`, as `SettableBeanProperty`
// short-circuits `null` handling. Hence need this check as well.
if (_primitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) {
ctxt.reportInputMismatch(this,
"Cannot map `null` into type %s (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)",
handledType().toString());
}
return _nullValue;
}
@Override
public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
return _emptyValue;
}
}
/*
/**********************************************************
/* Then primitive/wrapper types
/**********************************************************
*/
@JacksonStdImpl
public final static class BooleanDeserializer
extends PrimitiveOrWrapperDeserializer<Boolean>
{
private static final long serialVersionUID = 1L;
final static BooleanDeserializer primitiveInstance = new BooleanDeserializer(Boolean.TYPE, Boolean.FALSE);
final static BooleanDeserializer wrapperInstance = new BooleanDeserializer(Boolean.class, null);
public BooleanDeserializer(Class<Boolean> cls, Boolean nvl)
{
super(cls, nvl, Boolean.FALSE);
}
@Override
public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_TRUE) {
return Boolean.TRUE;
}
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
return _parseBoolean(p, ctxt);
}
// Since we can never have type info ("natural type"; String, Boolean, Integer, Double):
// (is it an error to even call this version?)
@Override
public Boolean deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException
{
JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_TRUE) {
return Boolean.TRUE;
}
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
return _parseBoolean(p, ctxt);
}
protected final Boolean _parseBoolean(JsonParser p, DeserializationContext ctxt)
throws IOException
{
JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_NULL) {
return (Boolean) _coerceNullToken(ctxt, _primitive);
}
if (t == JsonToken.START_ARRAY) { // unwrapping?
return _deserializeFromArray(p, ctxt);
}
// should accept ints too, (0 == false, otherwise true)
if (t == JsonToken.VALUE_NUMBER_INT) {
return Boolean.valueOf(_parseBooleanFromInt(p, ctxt));
}
// And finally, let's allow Strings to be converted too
if (t == JsonToken.VALUE_STRING) {
String text = p.getText().trim();
// [databind#422]: Allow aliases
if ("true".equals(text) || "True".equals(text)) {
_verifyStringForScalarCoercion(ctxt, text);
return Boolean.TRUE;
}
if ("false".equals(text) || "False".equals(text)) {
_verifyStringForScalarCoercion(ctxt, text);
return Boolean.FALSE;
}
if (text.length() == 0) {
return (Boolean) _coerceEmptyString(ctxt, _primitive);
}
if (_hasTextualNull(text)) {
return (Boolean) _coerceTextualNull(ctxt, _primitive);
}
return (Boolean) ctxt.handleWeirdStringValue(_valueClass, text,
"only \"true\" or \"false\" recognized");
}
// usually caller should have handled but:
if (t == JsonToken.VALUE_TRUE) {
return Boolean.TRUE;
}
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
// Otherwise, no can do:
return (Boolean) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
@JacksonStdImpl
public static class ByteDeserializer
extends PrimitiveOrWrapperDeserializer<Byte>
{
private static final long serialVersionUID = 1L;
final static ByteDeserializer primitiveInstance = new ByteDeserializer(Byte.TYPE, (byte) 0);
final static ByteDeserializer wrapperInstance = new ByteDeserializer(Byte.class, null);
public ByteDeserializer(Class<Byte> cls, Byte nvl)
{
super(cls, nvl, (byte) 0);
}
@Override
public Byte deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
return p.getByteValue();
}
return _parseByte(p, ctxt);
}
protected Byte _parseByte(JsonParser p, DeserializationContext ctxt) throws IOException
{
JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
String text = p.getText().trim();
if (_hasTextualNull(text)) {
return (Byte) _coerceTextualNull(ctxt, _primitive);
}
int len = text.length();
if (len == 0) {
return (Byte) _coerceEmptyString(ctxt, _primitive);
}
_verifyStringForScalarCoercion(ctxt, text);
int value;
try {
value = NumberInput.parseInt(text);
} catch (IllegalArgumentException iae) {
return (Byte) ctxt.handleWeirdStringValue(_valueClass, text,
"not a valid Byte value");
}
// So far so good: but does it fit?
// as per [JACKSON-804], allow range up to 255, inclusive
if (_byteOverflow(value)) {
return (Byte) ctxt.handleWeirdStringValue(_valueClass, text,
"overflow, value cannot be represented as 8-bit value");
// fall-through for deferred fails
}
return Byte.valueOf((byte) value);
}
if (t == JsonToken.VALUE_NUMBER_FLOAT) {
if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
_failDoubleToIntCoercion(p, ctxt, "Byte");
}
return p.getByteValue();
}
if (t == JsonToken.VALUE_NULL) {
return (Byte) _coerceNullToken(ctxt, _primitive);
}
// [databind#381]
if (t == JsonToken.START_ARRAY) {
return _deserializeFromArray(p, ctxt);
}
if (t == JsonToken.VALUE_NUMBER_INT) { // shouldn't usually be called with it but
return p.getByteValue();
}
return (Byte) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
@JacksonStdImpl
public static class ShortDeserializer
extends PrimitiveOrWrapperDeserializer<Short>
{
private static final long serialVersionUID = 1L;
final static ShortDeserializer primitiveInstance = new ShortDeserializer(Short.TYPE, Short.valueOf((short)0));
final static ShortDeserializer wrapperInstance = new ShortDeserializer(Short.class, null);
public ShortDeserializer(Class<Short> cls, Short nvl)
{
super(cls, nvl, (short)0);
}
@Override
public Short deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException
{
return _parseShort(p, ctxt);
}
protected Short _parseShort(JsonParser p, DeserializationContext ctxt) throws IOException
{
JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_NUMBER_INT) {
return p.getShortValue();
}
if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
String text = p.getText().trim();
int len = text.length();
if (len == 0) {
return (Short) _coerceEmptyString(ctxt, _primitive);
}
if (_hasTextualNull(text)) {
return (Short) _coerceTextualNull(ctxt, _primitive);
}
_verifyStringForScalarCoercion(ctxt, text);
int value;
try {
value = NumberInput.parseInt(text);
} catch (IllegalArgumentException iae) {
return (Short) ctxt.handleWeirdStringValue(_valueClass, text,
"not a valid Short value");
}
// So far so good: but does it fit?
if (_shortOverflow(value)) {
return (Short) ctxt.handleWeirdStringValue(_valueClass, text,
"overflow, value cannot be represented as 16-bit value");
}
return Short.valueOf((short) value);
}
if (t == JsonToken.VALUE_NUMBER_FLOAT) {
if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
_failDoubleToIntCoercion(p, ctxt, "Short");
}
return p.getShortValue();
}
if (t == JsonToken.VALUE_NULL) {
return (Short) _coerceNullToken(ctxt, _primitive);
}
if (t == JsonToken.START_ARRAY) {
return _deserializeFromArray(p, ctxt);
}
return (Short) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
@JacksonStdImpl
public static class CharacterDeserializer
extends PrimitiveOrWrapperDeserializer<Character>
{
private static final long serialVersionUID = 1L;
final static CharacterDeserializer primitiveInstance = new CharacterDeserializer(Character.TYPE, '\0');
final static CharacterDeserializer wrapperInstance = new CharacterDeserializer(Character.class, null);
public CharacterDeserializer(Class<Character> cls, Character nvl)
{
super(cls, nvl, '\0');
}
@Override
public Character deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException
{
switch (p.getCurrentTokenId()) {
case JsonTokenId.ID_NUMBER_INT: // ok iff ascii value
_verifyNumberForScalarCoercion(ctxt, p);
int value = p.getIntValue();
if (value >= 0 && value <= 0xFFFF) {
return Character.valueOf((char) value);
}
break;
case JsonTokenId.ID_STRING: // this is the usual type
// But does it have to be exactly one char?
String text = p.getText();
if (text.length() == 1) {
return Character.valueOf(text.charAt(0));
}
// actually, empty should become null?
if (text.length() == 0) {
return (Character) _coerceEmptyString(ctxt, _primitive);
}
break;
case JsonTokenId.ID_NULL:
return (Character) _coerceNullToken(ctxt, _primitive);
case JsonTokenId.ID_START_ARRAY:
return _deserializeFromArray(p, ctxt);
default:
}
return (Character) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
@JacksonStdImpl
public final static class IntegerDeserializer
extends PrimitiveOrWrapperDeserializer<Integer>
{
private static final long serialVersionUID = 1L;
final static IntegerDeserializer primitiveInstance = new IntegerDeserializer(Integer.TYPE, 0);
final static IntegerDeserializer wrapperInstance = new IntegerDeserializer(Integer.class, null);
public IntegerDeserializer(Class<Integer> cls, Integer nvl) {
super(cls, nvl, 0);
}
// since 2.6, slightly faster lookups for this very common type
@Override
public boolean isCachable() { return true; }
@Override
public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
return p.getIntValue();
}
return _parseInteger(p, ctxt);
}
// Since we can never have type info ("natural type"; String, Boolean, Integer, Double):
// (is it an error to even call this version?)
@Override
public Integer deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeserializer) throws IOException
{
if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
return p.getIntValue();
}
return _parseInteger(p, ctxt);
}
protected final Integer _parseInteger(JsonParser p, DeserializationContext ctxt) throws IOException
{
switch (p.getCurrentTokenId()) {
// NOTE: caller assumed to usually check VALUE_NUMBER_INT in fast path
case JsonTokenId.ID_NUMBER_INT:
return Integer.valueOf(p.getIntValue());
case JsonTokenId.ID_NUMBER_FLOAT: // coercing may work too
if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
_failDoubleToIntCoercion(p, ctxt, "Integer");
}
return Integer.valueOf(p.getValueAsInt());
case JsonTokenId.ID_STRING: // let's do implicit re-parse
String text = p.getText().trim();
int len = text.length();
if (len == 0) {
return (Integer) _coerceEmptyString(ctxt, _primitive);
}
if (_hasTextualNull(text)) {
return (Integer) _coerceTextualNull(ctxt, _primitive);
}
_verifyStringForScalarCoercion(ctxt, text);
try {
if (len > 9) {
long l = Long.parseLong(text);
if (_intOverflow(l)) {
return (Integer) ctxt.handleWeirdStringValue(_valueClass, text, String.format(
"Overflow: numeric value (%s) out of range of Integer (%d - %d)",
text, Integer.MIN_VALUE, Integer.MAX_VALUE));
}
return Integer.valueOf((int) l);
}
return Integer.valueOf(NumberInput.parseInt(text));
} catch (IllegalArgumentException iae) {
return (Integer) ctxt.handleWeirdStringValue(_valueClass, text,
"not a valid Integer value");
}
case JsonTokenId.ID_NULL:
return (Integer) _coerceNullToken(ctxt, _primitive);
case JsonTokenId.ID_START_ARRAY:
return _deserializeFromArray(p, ctxt);
}
// Otherwise, no can do:
return (Integer) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
@JacksonStdImpl
public final static class LongDeserializer
extends PrimitiveOrWrapperDeserializer<Long>
{
private static final long serialVersionUID = 1L;
final static LongDeserializer primitiveInstance = new LongDeserializer(Long.TYPE, 0L);
final static LongDeserializer wrapperInstance = new LongDeserializer(Long.class, null);
public LongDeserializer(Class<Long> cls, Long nvl) {
super(cls, nvl, 0L);
}
// since 2.6, slightly faster lookups for this very common type
@Override
public boolean isCachable() { return true; }
@Override
public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
return p.getLongValue();
}
return _parseLong(p, ctxt);
}
protected final Long _parseLong(JsonParser p, DeserializationContext ctxt) throws IOException
{
switch (p.getCurrentTokenId()) {
// NOTE: caller assumed to usually check VALUE_NUMBER_INT in fast path
case JsonTokenId.ID_NUMBER_INT:
return p.getLongValue();
case JsonTokenId.ID_NUMBER_FLOAT:
if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
_failDoubleToIntCoercion(p, ctxt, "Long");
}
return p.getValueAsLong();
case JsonTokenId.ID_STRING:
String text = p.getText().trim();
if (text.length() == 0) {
return (Long) _coerceEmptyString(ctxt, _primitive);
}
if (_hasTextualNull(text)) {
return (Long) _coerceTextualNull(ctxt, _primitive);
}
_verifyStringForScalarCoercion(ctxt, text);
// let's allow Strings to be converted too
try {
return Long.valueOf(NumberInput.parseLong(text));
} catch (IllegalArgumentException iae) { }
return (Long) ctxt.handleWeirdStringValue(_valueClass, text,
"not a valid Long value");
// fall-through
case JsonTokenId.ID_NULL:
return (Long) _coerceNullToken(ctxt, _primitive);
case JsonTokenId.ID_START_ARRAY:
return _deserializeFromArray(p, ctxt);
}
// Otherwise, no can do:
return (Long) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
@JacksonStdImpl
public static class FloatDeserializer
extends PrimitiveOrWrapperDeserializer<Float>
{
private static final long serialVersionUID = 1L;
final static FloatDeserializer primitiveInstance = new FloatDeserializer(Float.TYPE, 0.f);
final static FloatDeserializer wrapperInstance = new FloatDeserializer(Float.class, null);
public FloatDeserializer(Class<Float> cls, Float nvl) {
super(cls, nvl, 0.f);
}
@Override
public Float deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
return _parseFloat(p, ctxt);
}
protected final Float _parseFloat(JsonParser p, DeserializationContext ctxt)
throws IOException
{
// We accept couple of different types; obvious ones first:
JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_NUMBER_FLOAT || t == JsonToken.VALUE_NUMBER_INT) { // coercing should work too
return p.getFloatValue();
}
// And finally, let's allow Strings to be converted too
if (t == JsonToken.VALUE_STRING) {
String text = p.getText().trim();
if ((text.length() == 0)) {
return (Float) _coerceEmptyString(ctxt, _primitive);
}
if (_hasTextualNull(text)) {
return (Float) _coerceTextualNull(ctxt, _primitive);
}
switch (text.charAt(0)) {
case 'I':
if (_isPosInf(text)) {
return Float.POSITIVE_INFINITY;
}
break;
case 'N':
if (_isNaN(text)) {
return Float.NaN;
}
break;
case '-':
if (_isNegInf(text)) {
return Float.NEGATIVE_INFINITY;
}
break;
}
_verifyStringForScalarCoercion(ctxt, text);
try {
return Float.parseFloat(text);
} catch (IllegalArgumentException iae) { }
return (Float) ctxt.handleWeirdStringValue(_valueClass, text,
"not a valid Float value");
}
if (t == JsonToken.VALUE_NULL) {
return (Float) _coerceNullToken(ctxt, _primitive);
}
if (t == JsonToken.START_ARRAY) {
return _deserializeFromArray(p, ctxt);
}
// Otherwise, no can do:
return (Float) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
@JacksonStdImpl
public static class DoubleDeserializer
extends PrimitiveOrWrapperDeserializer<Double>
{
private static final long serialVersionUID = 1L;
final static DoubleDeserializer primitiveInstance = new DoubleDeserializer(Double.TYPE, 0.d);
final static DoubleDeserializer wrapperInstance = new DoubleDeserializer(Double.class, null);
public DoubleDeserializer(Class<Double> cls, Double nvl) {
super(cls, nvl, 0.d);
}
@Override
public Double deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return _parseDouble(p, ctxt);
}
// Since we can never have type info ("natural type"; String, Boolean, Integer, Double):
// (is it an error to even call this version?)
@Override
public Double deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeserializer) throws IOException
{
return _parseDouble(p, ctxt);
}
protected final Double _parseDouble(JsonParser p, DeserializationContext ctxt) throws IOException
{
JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
return p.getDoubleValue();
}
if (t == JsonToken.VALUE_STRING) {
String text = p.getText().trim();
if ((text.length() == 0)) {
return (Double) _coerceEmptyString(ctxt, _primitive);
}
if (_hasTextualNull(text)) {
return (Double) _coerceTextualNull(ctxt, _primitive);
}
switch (text.charAt(0)) {
case 'I':
if (_isPosInf(text)) {
return Double.POSITIVE_INFINITY;
}
break;
case 'N':
if (_isNaN(text)) {
return Double.NaN;
}
break;
case '-':
if (_isNegInf(text)) {
return Double.NEGATIVE_INFINITY;
}
break;
}
_verifyStringForScalarCoercion(ctxt, text);
try {
return parseDouble(text);
} catch (IllegalArgumentException iae) { }
return (Double) ctxt.handleWeirdStringValue(_valueClass, text,
"not a valid Double value");
}
if (t == JsonToken.VALUE_NULL) {
return (Double) _coerceNullToken(ctxt, _primitive);
}
if (t == JsonToken.START_ARRAY) {
return _deserializeFromArray(p, ctxt);
}
// Otherwise, no can do:
return (Double) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
For type Number.class
, we can just rely on type mappings that plain JsonParser.getNumberValue
returns. There is one additional complication: some numeric types (specifically, int/Integer and double/Double) are "non-typed"; meaning that they will NEVER be output with type information. But other numeric types may need such type information. This is why deserializeWithType
must be overridden.
/**
* For type <code>Number.class</code>, we can just rely on type
* mappings that plain {@link JsonParser#getNumberValue} returns.
*<p>
* There is one additional complication: some numeric
* types (specifically, int/Integer and double/Double) are "non-typed";
* meaning that they will NEVER be output with type information.
* But other numeric types may need such type information.
* This is why {@link #deserializeWithType} must be overridden.
*/
@SuppressWarnings("serial")
@JacksonStdImpl
public static class NumberDeserializer
extends StdScalarDeserializer<Object>
{
public final static NumberDeserializer instance = new NumberDeserializer();
public NumberDeserializer() {
super(Number.class);
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
switch (p.getCurrentTokenId()) {
case JsonTokenId.ID_NUMBER_INT:
if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) {
return _coerceIntegral(p, ctxt);
}
return p.getNumberValue();
case JsonTokenId.ID_NUMBER_FLOAT:
if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
// 10-Mar-2017, tatu: NaN and BigDecimal won't mix...
if (!p.isNaN()) {
return p.getDecimalValue();
}
}
return p.getNumberValue();
case JsonTokenId.ID_STRING:
/* Textual values are more difficult... not parsing itself, but figuring
* out 'minimal' type to use
*/
String text = p.getText().trim();
if ((text.length() == 0)) {
// note: no need to call `coerce` as this is never primitive
return getNullValue(ctxt);
}
if (_hasTextualNull(text)) {
// note: no need to call `coerce` as this is never primitive
return getNullValue(ctxt);
}
if (_isPosInf(text)) {
return Double.POSITIVE_INFINITY;
}
if (_isNegInf(text)) {
return Double.NEGATIVE_INFINITY;
}
if (_isNaN(text)) {
return Double.NaN;
}
_verifyStringForScalarCoercion(ctxt, text);
try {
if (!_isIntNumber(text)) {
if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
return new BigDecimal(text);
}
return Double.valueOf(text);
}
if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) {
return new BigInteger(text);
}
long value = Long.parseLong(text);
if (!ctxt.isEnabled(DeserializationFeature.USE_LONG_FOR_INTS)) {
if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) {
return Integer.valueOf((int) value);
}
}
return Long.valueOf(value);
} catch (IllegalArgumentException iae) {
return ctxt.handleWeirdStringValue(_valueClass, text,
"not a valid number");
}
case JsonTokenId.ID_START_ARRAY:
return _deserializeFromArray(p, ctxt);
}
// Otherwise, no can do:
return ctxt.handleUnexpectedToken(_valueClass, p);
}
As mentioned in class Javadoc, there is additional complexity in
handling potentially mixed type information here. Because of this,
we must actually check for "raw" integers and doubles first, before
calling type deserializer.
/**
* As mentioned in class Javadoc, there is additional complexity in
* handling potentially mixed type information here. Because of this,
* we must actually check for "raw" integers and doubles first, before
* calling type deserializer.
*/
@Override
public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException
{
switch (p.getCurrentTokenId()) {
case JsonTokenId.ID_NUMBER_INT:
case JsonTokenId.ID_NUMBER_FLOAT:
case JsonTokenId.ID_STRING:
// cannot point to type information: hence must be non-typed (int/double)
return deserialize(p, ctxt);
}
return typeDeserializer.deserializeTypedFromScalar(p, ctxt);
}
}
/*
/**********************************************************
/* And then bit more complicated (but non-structured) number
/* types
/**********************************************************
*/
This is bit trickier to implement efficiently, while avoiding
overflow problems.
/**
* This is bit trickier to implement efficiently, while avoiding
* overflow problems.
*/
@SuppressWarnings("serial")
@JacksonStdImpl
public static class BigIntegerDeserializer
extends StdScalarDeserializer<BigInteger>
{
public final static BigIntegerDeserializer instance = new BigIntegerDeserializer();
public BigIntegerDeserializer() { super(BigInteger.class); }
@Override
public Object getEmptyValue(DeserializationContext ctxt) {
return BigInteger.ZERO;
}
@SuppressWarnings("incomplete-switch")
@Override
public BigInteger deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
switch (p.getCurrentTokenId()) {
case JsonTokenId.ID_NUMBER_INT:
switch (p.getNumberType()) {
case INT:
case LONG:
case BIG_INTEGER:
return p.getBigIntegerValue();
}
break;
case JsonTokenId.ID_NUMBER_FLOAT:
if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
_failDoubleToIntCoercion(p, ctxt, "java.math.BigInteger");
}
return p.getDecimalValue().toBigInteger();
case JsonTokenId.ID_START_ARRAY:
return _deserializeFromArray(p, ctxt);
case JsonTokenId.ID_STRING: // let's do implicit re-parse
String text = p.getText().trim();
// note: no need to call `coerce` as this is never primitive
if (_isEmptyOrTextualNull(text)) {
_verifyNullForScalarCoercion(ctxt, text);
return getNullValue(ctxt);
}
_verifyStringForScalarCoercion(ctxt, text);
try {
return new BigInteger(text);
} catch (IllegalArgumentException iae) { }
return (BigInteger) ctxt.handleWeirdStringValue(_valueClass, text,
"not a valid representation");
}
// String is ok too, can easily convert; otherwise, no can do:
return (BigInteger) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
@SuppressWarnings("serial")
@JacksonStdImpl
public static class BigDecimalDeserializer
extends StdScalarDeserializer<BigDecimal>
{
public final static BigDecimalDeserializer instance = new BigDecimalDeserializer();
public BigDecimalDeserializer() { super(BigDecimal.class); }
@Override
public Object getEmptyValue(DeserializationContext ctxt) {
return BigDecimal.ZERO;
}
@Override
public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException
{
switch (p.getCurrentTokenId()) {
case JsonTokenId.ID_NUMBER_INT:
case JsonTokenId.ID_NUMBER_FLOAT:
return p.getDecimalValue();
case JsonTokenId.ID_STRING:
String text = p.getText().trim();
// note: no need to call `coerce` as this is never primitive
if (_isEmptyOrTextualNull(text)) {
_verifyNullForScalarCoercion(ctxt, text);
return getNullValue(ctxt);
}
_verifyStringForScalarCoercion(ctxt, text);
try {
return new BigDecimal(text);
} catch (IllegalArgumentException iae) { }
return (BigDecimal) ctxt.handleWeirdStringValue(_valueClass, text,
"not a valid representation");
case JsonTokenId.ID_START_ARRAY:
return _deserializeFromArray(p, ctxt);
}
// Otherwise, no can do:
return (BigDecimal) ctxt.handleUnexpectedToken(_valueClass, p);
}
}
}