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, "Can not 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 can not 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 can not 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 <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. */ @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: // can not 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. */ @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); } } }