Search code examples
javajsonjacksonjackson-databind

Deserialize Lists and Objects to the same Structure with Jackson


As input for my Application I might get either a single JsonObject, or a List of them:

input1 = [ { "prop": "val1" }, { "prop": "val2" } ]
input2 = { "prop": "val" }

I can use JsonNode as target type for both inputs

objectMapper.readValue(input1, JsonNode.class);
objectMapper.readValue(input2, JsonNode.class);

And then evaluate whether the root node is a ArrayNode or ObjectNode.

I seek a way to define my custom target type, like a List<MyObject> which has one Element if a JsonObject is provided, or zero to multiple, if a List is provided.

objectMapper.readValue(input, new TypeRef<ArrayList<MyObject>>() {});

however fails for the single object - it can not construc an Array-Type from {.

I was trying to create my own type:

public class MyList extends ArrayList<MyObject> {
    public String prop;

    @JsonCreator
    public MyList(String prop) {
        super();
        this.prop = prop; // Resp add(new MyObject(prop));
    }

    public MyList() {}
}

But Jackson refuses to use the JsonCreator for single objects.

Is there any way, I could do that (ideally without a custom serializer, unless that one can be made pretty generic)


Solution

  • Of course, Jackson has an easy solution for that:
    DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY to your help!

    @EqualsAndHashCode
    public class Example {
        @JsonProperty public String name
    }
    
    @Test
    public void experiment() {
        ObjectMapper om = new ObjectMapper();
        om.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
    
        String list= "[{ \"name\": \"peter\" }]";
        String single= "{ \"name\": \"peter\" }";
    
        List<Example> respList = om.readValue(list, new TypeReference<List<Example>>() {});
        List<Example> respSingle = om.readValue(single, new TypeReference<List<Example>>() {}); 
        Assert.assertEquals(respList, respSingle)
    }