derM
derM

Reputation: 13701

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)

Upvotes: 3

Views: 1015

Answers (1)

derM
derM

Reputation: 13701

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)
}

Upvotes: 1

Related Questions