| 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.databind.*; |
| import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; |
| import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; |
| |
| /** |
| * 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; |
| protected final boolean _primitive; |
| |
| protected PrimitiveOrWrapperDeserializer(Class<T> vc, T nvl) { |
| super(vc); |
| _nullValue = nvl; |
| _primitive = vc.isPrimitive(); |
| } |
| |
| @Override |
| public final T getNullValue(DeserializationContext ctxt) throws JsonMappingException |
| { |
| if (_primitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) { |
| ctxt.reportMappingException( |
| "Can not map JSON null into type %s (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)", |
| handledType().toString()); |
| } |
| return _nullValue; |
| } |
| |
| @Override |
| public T getEmptyValue(DeserializationContext ctxt) throws JsonMappingException { |
| // [databind#1095]: Should not allow coercion from into null from Empty String |
| // either, if `null` not allowed |
| if (_primitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) { |
| ctxt.reportMappingException( |
| "Can not map Empty String as null into type %s (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)", |
| handledType().toString()); |
| } |
| return _nullValue; |
| } |
| } |
| |
| /* |
| /********************************************************** |
| /* 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); |
| } |
| |
| @Override |
| public Boolean deserialize(JsonParser j, DeserializationContext ctxt) throws IOException |
| { |
| return _parseBoolean(j, 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 |
| { |
| return _parseBoolean(p, ctxt); |
| } |
| } |
| |
| @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); |
| } |
| |
| @Override |
| public Byte deserialize(JsonParser p, DeserializationContext ctxt) throws IOException |
| { |
| return _parseByte(p, ctxt); |
| } |
| } |
| |
| @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); |
| } |
| |
| @Override |
| public Short deserialize(JsonParser jp, DeserializationContext ctxt) |
| throws IOException |
| { |
| return _parseShort(jp, ctxt); |
| } |
| } |
| |
| @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); |
| } |
| |
| @Override |
| public Character deserialize(JsonParser p, DeserializationContext ctxt) |
| throws IOException |
| { |
| switch (p.getCurrentTokenId()) { |
| case JsonTokenId.ID_NUMBER_INT: // ok iff ascii value |
| 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) getEmptyValue(ctxt); |
| } |
| break; |
| 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, Integer.valueOf(0)); |
| final static IntegerDeserializer wrapperInstance = new IntegerDeserializer(Integer.class, null); |
| |
| public IntegerDeserializer(Class<Integer> cls, Integer nvl) { |
| super(cls, nvl); |
| } |
| |
| // 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); |
| } |
| } |
| |
| @JacksonStdImpl |
| public final static class LongDeserializer |
| extends PrimitiveOrWrapperDeserializer<Long> |
| { |
| private static final long serialVersionUID = 1L; |
| |
| final static LongDeserializer primitiveInstance = new LongDeserializer(Long.TYPE, Long.valueOf(0L)); |
| final static LongDeserializer wrapperInstance = new LongDeserializer(Long.class, null); |
| |
| public LongDeserializer(Class<Long> cls, Long nvl) { |
| super(cls, nvl); |
| } |
| |
| // 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); |
| } |
| } |
| |
| @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); |
| } |
| |
| @Override |
| public Float deserialize(JsonParser p, DeserializationContext ctxt) throws IOException |
| { |
| return _parseFloat(p, ctxt); |
| } |
| } |
| |
| @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); |
| } |
| |
| @Override |
| public Double deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { |
| return _parseDouble(jp, 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 jp, DeserializationContext ctxt, |
| TypeDeserializer typeDeserializer) throws IOException |
| { |
| return _parseDouble(jp, ctxt); |
| } |
| } |
| |
| /** |
| * 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)) { |
| 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) { |
| return getEmptyValue(ctxt); |
| } |
| if (_hasTextualNull(text)) { |
| return getNullValue(ctxt); |
| } |
| if (_isPosInf(text)) { |
| return Double.POSITIVE_INFINITY; |
| } |
| if (_isNegInf(text)) { |
| return Double.NEGATIVE_INFINITY; |
| } |
| if (_isNaN(text)) { |
| return Double.NaN; |
| } |
| try { |
| if (!_isIntNumber(text)) { |
| if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { |
| return new BigDecimal(text); |
| } |
| return new Double(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 jp, DeserializationContext ctxt, |
| TypeDeserializer typeDeserializer) |
| throws IOException |
| { |
| switch (jp.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(jp, ctxt); |
| } |
| return typeDeserializer.deserializeTypedFromScalar(jp, 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); } |
| |
| @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(); |
| if (text.length() == 0) { |
| return null; |
| } |
| 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 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(); |
| if (text.length() == 0) { |
| return null; |
| } |
| 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); |
| } |
| } |
| } |