Search code examples
javajacksondeserialization

Jackson deserialization of type with different objects


I have a result from a web service that returns either a boolean value or a singleton map, e.g.

Boolean result:

{
    id: 24428,
    rated: false
}

Map result:

{
    id: 78,
    rated: {
        value: 10
    }
}

Individually I can map both of these easily, but how do I do it generically?

Basically I want to map it to a class like:

public class Rating {
    private int id;
    private int rated;
    ...
    public void setRated(?) {
        // if value == false, set rated = -1;
        // else decode "value" as rated
    }
}

All of the polymorphic examples use @JsonTypeInfo to map based on a property in the data, but I don't have that option in this case.


EDIT
The updated section of code:

@JsonProperty("rated")
public void setRating(JsonNode ratedNode) {
    JsonNode valueNode = ratedNode.get("value");
    // if the node doesn't exist then it's the boolean value
    if (valueNode == null) {
        // Use a default value
        this.rating = -1;
    } else {
        // Convert the value to an integer
        this.rating = valueNode.asInt();
    }
}

Solution

  • No no no. You do NOT have to write a custom deserializer. Just use "untyped" mapping first:

    public class Response {
      public long id;
      public Object rated;
    }
    // OR
    public class Response {
      public long id;
      public JsonNode rated;
    }
    Response r = mapper.readValue(source, Response.class);
    

    which gives value of Boolean or java.util.Map for "rated" (with first approach); or a JsonNode in second case.

    From that, you can either access data as is, or, perhaps more interestingly, convert to actual value:

    if (r.rated instanceof Boolean) {
        // handle that
    } else {
        ActualRated actual = mapper.convertValue(r.rated, ActualRated.class);
    }
    // or, if you used JsonNode, use "mapper.treeToValue(ActualRated.class)
    

    There are other kinds of approaches too -- using creator "ActualRated(boolean)", to let instance constructed either from POJO, or from scalar. But I think above should work.