Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Deserialize null to Double.NaN (Java, Jackson)

Question: The Jackson ObjectMapper deserializer is converting a null value to a 0 for a Double field. I need it to either be deserialized to null or Double.NaN. How can I do this?

Do I need to write a custom Double deserializer that maps null to Double.NaN?

Already tried: I have scoured the DeserializationFeature Enum but I don't think anything applies. (http://fasterxml.github.io/jackson-databind/javadoc/2.0.0/com/fasterxml/jackson/databind/DeserializationFeature.html#FAIL_ON_NULL_FOR_PRIMITIVES)

Motivation: I am deserializing a json object into a custom object (Thing) with code similar to the following. I need the deserializer to keep the value as null or change it to Double.NaN because I need to be able to differential between the 0 case (located at latitude/longitude/altitude = 0) and the null/Double.NaN case (when these values are unavailable).

Jackson deserializing

try {
        ObjectMapper mapper = new ObjectMapper();
        Thing t = mapper.readValue(new File("foobar/json.txt"), Thing.class);

    } catch (JsonParseException e) {
        ...do stuff..
    }

Contents of json.txt. Note that the value null is actually written in the file. It is not left empty. It is not the empty string. It is actuall the word null.

{
  "thing" : {
    "longitude" : null,
    "latitude" : null,
    "altitude" : null
  }
}

Code for Thing

import java.io.Serializable;

public class Thing implements Serializable {

    private static final long serialVersionUID = 1L;

    Double latitude;
    Double longitude;
    Double altitude;

    public Thing(Double latitude, Double longitude, Double altitude) {
         this.latitude = latitude;
         this.longitude = longitude;
         this.altitude = altitude; 

    }
    ...rest of code...
}
like image 689
miss.serena Avatar asked Mar 10 '15 23:03

miss.serena


2 Answers

The solution that worked for me was to make a custom JSON Deserializer that transformed null into Double.NaN. Adapting what I wrote to match my example code above it would look like this.

public class ThingDeserializer extends JsonDeserializer<Thing> {

@Override
public Thing deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
        JsonProcessingException {

    Thing thingy = new Thing();

    JsonNode node = jp.getCodec().readTree(jp);
    if (node.get("latitude").isNull()) {
        thingy.setLatitude(Double.NaN);
    } else {
       thingy.setLatitude(node.get("latitude").asDouble());
    }
    if (node.get("longitude").isNull()) {
        thingy.setLongitude(Double.NaN);
    } else {
        thingy.setLongitude(node.get("longitude").asDouble());
    }
    if (node.get("altitude").isNull()) {
        thingy.setAltitude(Double.NaN);
    } else {
        thingy.setLatitude(node.get("altitude").asDouble());
    }
    return thingy;
}

then I registered the deserializer in the class Thing by adding the annotation above the class declaration.

@JsonDeserialize(using = ThingDeserializer.class)
public class Thing implements Serializable { 
            ... class code here ... 
}

Note I think a better answer would be to deserialize the Double class rather than the Thing class. By deserializing the Double you could generalize the conversion from null to NaN. This would also do away with pulling the specific fields from the node by field name. I could not figure out how to do it on a limited time budget so this is what worked for me. Also, the deserialization is actually being implicitly called by Jackson through a REST api so I am not sure how/if this changes things. I would love to see a solution that would accomplish this though.

like image 81
miss.serena Avatar answered Oct 16 '22 13:10

miss.serena


This is what I did:

public class DoubleDeserializer extends JsonDeserializer<Double> {

    @Override
    public Double deserialize(JsonParser parser, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        String doubleStr = parser.getText();

        if (doubleStr.isEmpty() || doubleStr == null) {
            return null;
        }

        return new Double(doubleStr);
    }
}

and then in my bean:

@JsonDeserialize(using = DoubleDeserializer.class)
private Double partialPressureCO2;

Hope this helps.

like image 27
skyman Avatar answered Oct 16 '22 12:10

skyman