简体   繁体   中英

Jackson Serialization and Deserialization of java.lang.Number Extensions

I am trying to use Jackson to serialize and deserialize a custom numeric class that extends java.lang.Number . Below is a barebones implementation of the class without extending Number .

import com.google.common.primitives;
public class UnsignedNumericClass {
    private long data;
    public UnsignedNumericClass(long data) {
        this.data = data;
    }
    public UnsignedNumericClass(String data) {
        this.data = UnsignedLongs.decode(data);
    }
    public UnsignedNumericClass() {}
    public void setData(long data) {this.data = data;}
    public long getData() {return data;}
    public toString() {return UnsignedLongs.toString(data);}

    public int intValue() {return (int) data;}
    public long longValue() {return data;}
    public float floatValue() {return data;}
    public double doubleValue() {return data;}
}

This plays very well with Jackson, however the second I change the class to read

public class UnsignedNumericClass extends Number

Jackson fails to deserialize claiming "No suitable creator method found to deserialize from Number value (14169630718280903901)". Additionally, it only fails sometimes.

Does anyone know why this is happening or how to make Jackson successfully deserialize to an instance of a class that extends java.lang.Number ?

davidxxx was indeed right that deserialization was only failing when the serialized number was greater than Long.MAX_VALUE . The problem runs a little deeper than that though.

When UnsignedNumericClass does not extend Number , Jackson treats it as an ordinary object and serializes/deserializes via getters, setters, and the default constructor UnsignedNumericClass() .

Once UnsignedNumericClass extends Number , Jackson changes to serialize/deserialize via toString() and the String-argument constructor UnsignedNumericClass(String data) . Note that an exception will be thrown if a String-argument constructor does not exist.

I was experiencing the questioned issue because I am implementing an unsigned 64-bit number. Because the number is unsigned, my toString() implementation prints the unsigned interpretation of the private long data field.

When deserializing numeric types, Jackson performs a check to make sure that it is a valid number. That is, it checks if the number given in the JSON document is less than Long.MAX_VALUE . If this is not the case, it relies on a custom deserializer instead of just using my String-argument constructor which could handle the number just fine. Since no custom deserializer is specified, it throws the "no suitable creator method" exception. Therefore the solution is to define a simple custom deserializer that uses the String-argument constructor:

public class CustomDeserializer extends StdDeserializer<UnsignedNumericClass> {
    public CustomDeserializer(){super(null);}
    public CustomDeserializer(Class<?> c){super(c);}
    public UnsignedNumericClass deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException{
        return new UnsignedNumericClass(jsonParser.getText());
}

Then we specify to Jackson to use this deserializer via the @JsonDeserialize() annotation:

@JsonDeserialize(using=CustomDeserializer.class)
public class UnsignedNumericClass extends Number

I think a custom deserializer for BigInteger is predefined in Jackson so this issue does not appear for that class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM