Search code examples
javajsonjacksonjson-deserialization

Unmarshall JSON to subtypes with different field type jackson


I receive a JSON array of objects which all have field content, but the type of this field may differ:

[
    {
        "id": "primaryBodyHeader",
        "type": "RichText",
        "content": "<h1>Alice's Adventures in Wonderland</h1>"
    },
    {
        "id": "1027",
        "type": "RichText",
        "content": {
            "value": "RVMtMTk=",
            "contentType": "DynamicContent"
        }
    }
]

And I have the bean:

public abstract class LandingPageContentItem {
    private String id;
    private String type;
    private String content;
}

At least I want to map content to a text field when it is a text (null for non-text content)

At most, I want to map different kinds of items to different subclasses according to the type of field content - TextContentItem, ComplexContentItem or so. @JsonSubTypes can't do this

Is there a way to do it without custom deserializer?


Solution

  • If you don't know (or have no control of) what might be in content field then I'd suggest you to map raw com.fasterxml.jackson.databind.JsonNode like this

    public static class LandingPageContentItem {
        private final String id;
        private final String type;
        private final JsonNode content;
    
        @JsonCreator
        public LandingPageContentItem(
                @JsonProperty("id") final String id, 
                @JsonProperty("type") final String type, 
                @JsonProperty("content") final JsonNode content) {
            this.id = id;
            this.type = type;
            this.content = content;
        }
    
        /* some logic here */
    }
    

    And then you can read it normally

    ObjectMapper mapper = new ObjectMapper();
    List<LandingPageContentItem> items = 
        mapper.readValue(node, new TypeReference<List<LandingPageContentItem>>() {});
    

    Later on you can verify if JsonNode is of expected type.

    if (content.isTextual()) {
        // do something with content.asText(); 
    }