package com.fasterxml.jackson.databind.deser.std; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationFeature; 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 { public static StdDeserializer<?>[] all() { return new StdDeserializer<?>[] { // primitive-wrappers (simple): new BooleanDeserializer(Boolean.class, null), new ByteDeserializer(Byte.class, null), new ShortDeserializer(Short.class, null), new CharacterDeserializer(Character.class, null), new IntegerDeserializer(Integer.class, null), new LongDeserializer(Long.class, null), new FloatDeserializer(Float.class, null), new DoubleDeserializer(Double.class, null), /* And actual primitives: difference is the way nulls are to be * handled... */ new BooleanDeserializer(Boolean.TYPE, Boolean.FALSE), new ByteDeserializer(Byte.TYPE, Byte.valueOf((byte)(0))), new ShortDeserializer(Short.TYPE, Short.valueOf((short)0)), new CharacterDeserializer(Character.TYPE, Character.valueOf('\0')), new IntegerDeserializer(Integer.TYPE, Integer.valueOf(0)), new LongDeserializer(Long.TYPE, Long.valueOf(0L)), new FloatDeserializer(Float.TYPE, Float.valueOf(0.0f)), new DoubleDeserializer(Double.TYPE, Double.valueOf(0.0)), // and related new NumberDeserializer(), new BigDecimalDeserializer(), new BigIntegerDeserializer() }; } /* /********************************************************** /* 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 PrimitiveOrWrapperDeserializer(Class<T> vc, T nvl) { super(vc); _nullValue = nvl; } @Override public final T getNullValue() { return _nullValue; } } /* /********************************************************** /* Then primitive/wrapper types /********************************************************** */ @JacksonStdImpl public final static class BooleanDeserializer extends PrimitiveOrWrapperDeserializer<Boolean> { private static final long serialVersionUID = 1L; public BooleanDeserializer(Class<Boolean> cls, Boolean nvl) { super(cls, nvl); } @Override public Boolean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { return _parseBoolean(jp, ctxt); } // 1.6: 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 jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException, JsonProcessingException { return _parseBoolean(jp, ctxt); } } @JacksonStdImpl public final static class ByteDeserializer extends PrimitiveOrWrapperDeserializer<Byte> { public ByteDeserializer(Class<Byte> cls, Byte nvl) { super(cls, nvl); } @Override public Byte deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { return _parseByte(jp, ctxt); } } @JacksonStdImpl public final static class ShortDeserializer extends PrimitiveOrWrapperDeserializer<Short> { public ShortDeserializer(Class<Short> cls, Short nvl) { super(cls, nvl); } @Override public Short deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { return _parseShort(jp, ctxt); } } @JacksonStdImpl public final static class CharacterDeserializer extends PrimitiveOrWrapperDeserializer<Character> { public CharacterDeserializer(Class<Character> cls, Character nvl) { super(cls, nvl); } @Override public Character deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { JsonToken t = jp.getCurrentToken(); int value; if (t == JsonToken.VALUE_NUMBER_INT) { // ok iff ascii value value = jp.getIntValue(); if (value >= 0 && value <= 0xFFFF) { return Character.valueOf((char) value); } } else if (t == JsonToken.VALUE_STRING) { // this is the usual type // But does it have to be exactly one char? String text = jp.getText(); if (text.length() == 1) { return Character.valueOf(text.charAt(0)); } // actually, empty should become null? if (text.length() == 0) { return (Character) getEmptyValue(); } } throw ctxt.mappingException(_valueClass, t); } } @JacksonStdImpl public final static class IntegerDeserializer extends PrimitiveOrWrapperDeserializer<Integer> { private static final long serialVersionUID = 1L; public IntegerDeserializer(Class<Integer> cls, Integer nvl) { super(cls, nvl); } @Override public Integer deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { return _parseInteger(jp, ctxt); } // 1.6: 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 jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException, JsonProcessingException { return _parseInteger(jp, ctxt); } } @JacksonStdImpl public final static class LongDeserializer extends PrimitiveOrWrapperDeserializer<Long> { private static final long serialVersionUID = 1L; public LongDeserializer(Class<Long> cls, Long nvl) { super(cls, nvl); } @Override public Long deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { return _parseLong(jp, ctxt); } } @JacksonStdImpl public final static class FloatDeserializer extends PrimitiveOrWrapperDeserializer<Float> { private static final long serialVersionUID = 1L; public FloatDeserializer(Class<Float> cls, Float nvl) { super(cls, nvl); } @Override public Float deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { /* 22-Jan-2009, tatu: Bounds/range checks would be tricky * here, so let's not bother even trying... */ return _parseFloat(jp, ctxt); } } @JacksonStdImpl public final static class DoubleDeserializer extends PrimitiveOrWrapperDeserializer<Double> { private static final long serialVersionUID = 1L; public DoubleDeserializer(Class<Double> cls, Double nvl) { super(cls, nvl); } @Override public Double deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { return _parseDouble(jp, ctxt); } // 1.6: 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, JsonProcessingException { return _parseDouble(jp, ctxt); } } /** * For type <code>Number.class</code>, we can just rely on type * mappings that plain {@link JsonParser#getNumberValue} returns. *<p> * Since 1.5, 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 final static class NumberDeserializer extends StdScalarDeserializer<Number> { public NumberDeserializer() { super(Number.class); } @Override public Number deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { JsonToken t = jp.getCurrentToken(); if (t == JsonToken.VALUE_NUMBER_INT) { if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) { return jp.getBigIntegerValue(); } return jp.getNumberValue(); } else if (t == JsonToken.VALUE_NUMBER_FLOAT) { /* [JACKSON-72]: need to allow overriding the behavior * regarding which type to use */ if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { return jp.getDecimalValue(); } return Double.valueOf(jp.getDoubleValue()); } /* Textual values are more difficult... not parsing itself, but figuring * out 'minimal' type to use */ if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse String text = jp.getText().trim(); try { if (text.indexOf('.') >= 0) { // floating point // as per [JACKSON-72]: if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { return new BigDecimal(text); } return new Double(text); } // as per [JACKSON-100]: if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) { return new BigInteger(text); } long value = Long.parseLong(text); if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) { return Integer.valueOf((int) value); } return Long.valueOf(value); } catch (IllegalArgumentException iae) { throw ctxt.weirdStringException(text, _valueClass, "not a valid number"); } } // Otherwise, no can do: throw ctxt.mappingException(_valueClass, t); } /** * 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, JsonProcessingException { switch (jp.getCurrentToken()) { case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: case VALUE_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 BigIntegerDeserializer() { super(BigInteger.class); } @Override public BigInteger deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { JsonToken t = jp.getCurrentToken(); String text; if (t == JsonToken.VALUE_NUMBER_INT) { switch (jp.getNumberType()) { case INT: case LONG: return BigInteger.valueOf(jp.getLongValue()); } } else if (t == JsonToken.VALUE_NUMBER_FLOAT) { /* Whether to fail if there's non-integer part? * Could do by calling BigDecimal.toBigIntegerExact() */ return jp.getDecimalValue().toBigInteger(); } else if (t != JsonToken.VALUE_STRING) { // let's do implicit re-parse // String is ok too, can easily convert; otherwise, no can do: throw ctxt.mappingException(_valueClass, t); } text = jp.getText().trim(); if (text.length() == 0) { return null; } try { return new BigInteger(text); } catch (IllegalArgumentException iae) { throw ctxt.weirdStringException(text, _valueClass, "not a valid representation"); } } } @SuppressWarnings("serial") @JacksonStdImpl public static class BigDecimalDeserializer extends StdScalarDeserializer<BigDecimal> { public BigDecimalDeserializer() { super(BigDecimal.class); } @Override public BigDecimal deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { JsonToken t = jp.getCurrentToken(); if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { return jp.getDecimalValue(); } // String is ok too, can easily convert if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse String text = jp.getText().trim(); if (text.length() == 0) { return null; } try { return new BigDecimal(text); } catch (IllegalArgumentException iae) { throw ctxt.weirdStringException(text, _valueClass, "not a valid representation"); } } // Otherwise, no can do: throw ctxt.mappingException(_valueClass, t); } } }