blob: e545d6b86935b094224376c78d857b81348ea1db [file] [log] [blame]
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);
}
}
}