com.fitbur.fasterxml.jackson.databind.deser.std.NumberDeserializers Maven / Gradle / Ivy
package com.fitbur.fasterxml.jackson.databind.com.fitburser.std;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.fitbur.fasterxml.jackson.core.JsonParser;
import com.fitbur.fasterxml.jackson.core.JsonProcessingException;
import com.fitbur.fasterxml.jackson.core.JsonToken;
import com.fitbur.fasterxml.jackson.databind.DeserializationContext;
import com.fitbur.fasterxml.jackson.databind.DeserializationFeature;
import com.fitbur.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fitbur.fasterxml.jackson.databind.jsontype.TypeDeserializer;
/**
* Container class for com.fitburserializers 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
extends StdScalarDeserializer
{
private static final long serialVersionUID = 1L;
protected final T _nullValue;
protected PrimitiveOrWrapperDeserializer(Class 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
{
private static final long serialVersionUID = 1L;
public BooleanDeserializer(Class cls, Boolean nvl)
{
super(cls, nvl);
}
@Override
public Boolean com.fitburserialize(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 com.fitburserializeWithType(JsonParser jp, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException, JsonProcessingException
{
return _parseBoolean(jp, ctxt);
}
}
@JacksonStdImpl
public final static class ByteDeserializer
extends PrimitiveOrWrapperDeserializer
{
public ByteDeserializer(Class cls, Byte nvl)
{
super(cls, nvl);
}
@Override
public Byte com.fitburserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
return _parseByte(jp, ctxt);
}
}
@JacksonStdImpl
public final static class ShortDeserializer
extends PrimitiveOrWrapperDeserializer
{
public ShortDeserializer(Class cls, Short nvl)
{
super(cls, nvl);
}
@Override
public Short com.fitburserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
return _parseShort(jp, ctxt);
}
}
@JacksonStdImpl
public final static class CharacterDeserializer
extends PrimitiveOrWrapperDeserializer
{
public CharacterDeserializer(Class cls, Character nvl)
{
super(cls, nvl);
}
@Override
public Character com.fitburserialize(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
{
private static final long serialVersionUID = 1L;
public IntegerDeserializer(Class cls, Integer nvl)
{
super(cls, nvl);
}
@Override
public Integer com.fitburserialize(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 com.fitburserializeWithType(JsonParser jp, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException, JsonProcessingException
{
return _parseInteger(jp, ctxt);
}
}
@JacksonStdImpl
public final static class LongDeserializer
extends PrimitiveOrWrapperDeserializer
{
private static final long serialVersionUID = 1L;
public LongDeserializer(Class cls, Long nvl)
{
super(cls, nvl);
}
@Override
public Long com.fitburserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
return _parseLong(jp, ctxt);
}
}
@JacksonStdImpl
public final static class FloatDeserializer
extends PrimitiveOrWrapperDeserializer
{
private static final long serialVersionUID = 1L;
public FloatDeserializer(Class cls, Float nvl)
{
super(cls, nvl);
}
@Override
public Float com.fitburserialize(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
{
private static final long serialVersionUID = 1L;
public DoubleDeserializer(Class cls, Double nvl)
{
super(cls, nvl);
}
@Override
public Double com.fitburserialize(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 com.fitburserializeWithType(JsonParser jp, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException, JsonProcessingException
{
return _parseDouble(jp, ctxt);
}
}
/**
* For type Number.class
, we can just rely on type
* mappings that plain {@link JsonParser#getNumberValue} returns.
*
* Since 1.5, there is one additional com.fitburplication: 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 #com.fitburserializeWithType} must be overridden.
*/
@SuppressWarnings("serial")
@JacksonStdImpl
public final static class NumberDeserializer
extends StdScalarDeserializer
{
public NumberDeserializer() { super(Number.class); }
@Override
public Number com.fitburserialize(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 com.fitburplexity in
* handling potentially mixed type information here. Because of this,
* we must actually check for "raw" integers and doubles first, before
* calling type com.fitburserializer.
*/
@Override
public Object com.fitburserializeWithType(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 com.fitburserialize(jp, ctxt);
}
return typeDeserializer.com.fitburserializeTypedFromScalar(jp, ctxt);
}
}
/*
/**********************************************************
/* And then bit more com.fitburplicated (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
{
public BigIntegerDeserializer() { super(BigInteger.class); }
@Override
public BigInteger com.fitburserialize(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
{
public BigDecimalDeserializer() { super(BigDecimal.class); }
@Override
public BigDecimal com.fitburserialize(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);
}
}
}