Jackson mapping Object or list of Object depending on json input
Asked Answered
S

2

16

I have this POJO :

public class JsonObj {

    private String id;
    private List<Location> location;


    public String getId() {
        return id;
    }

    public List<Location> getLocation() {
        return location;
    }

    @JsonSetter("location")
    public void setLocation(){
        List<Location> list = new ArrayList<Location>();
        if(location instanceof Location){
            list.add((Location) location);
            location = list;
        }
    }
}

the "location" object from the json input can be either a simple instance of Location or an Array of Location. When it is just one instance, I get this error :

Could not read JSON: Can not deserialize instance of java.util.ArrayList out of   START_OBJECT token

I've tried to implement a custom setter but it didn't work. How could I do to map either a Location or a List depending on the json input?

Spurgeon answered 11/1, 2014 at 14:20 Comment(7)
Different JSON means different JSON DTO. If your JSON has an [] you need to use List, if it has a {} you need a simple object.Equable
It is sometimes [] and sometimes {} for the same key and unfortunally I can not change this.Spurgeon
Create a custom deserializer that checks if its a [] or a {} and appropriately creates a instance and adds it to the List.Equable
It seems to be the solution. Could you provide some code or doc in order to help me implement this?Spurgeon
Just google jackson deserializer. Something like this.Equable
I'm sorry but I'm new to jackson mapping and I didn't succeed to create this deserializer. See my question #21064503 if you wish to help me. Thank you anyway!Spurgeon
I found this answer useful if you want to do this on a single propertyEmogeneemollient
A
25

Update: Mher Sarkissian's soulution works fine, it can also be used with annotations as suggested here, like so:.

@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List<Item> item;

My deepest sympathies for this most annoying problem, I had just the same problem and found the solution here: https://mcmap.net/q/548789/-jackson-deserialize-object-or-array

With a little modification I come up with this, first the generic class:

public abstract class OptionalArrayDeserializer<T> extends JsonDeserializer<List<T>> {

    private final Class<T> clazz;

    public OptionalArrayDeserializer(Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    public List<T> deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException {
        ObjectCodec oc = jp.getCodec();
        JsonNode node = oc.readTree(jp);
        ArrayList<T> list = new ArrayList<>();
        if (node.isArray()) {
            for (JsonNode elementNode : node) {
                list.add(oc.treeToValue(elementNode, clazz));
            }
        } else {
            list.add(oc.treeToValue(node, clazz));
        }
        return list;
    }
}

And then the property and the actual deserializer class (Java generics is not always pretty):

@JsonDeserialize(using = ItemListDeserializer.class)
private List<Item> item;

public static class ItemListDeserializer extends OptionalArrayDeserializer<Item> {
    protected ItemListDeserializer() {
        super(Item.class);
    }
}
Adams answered 19/2, 2015 at 19:36 Comment(1)
Thanks for this snippet, it solved my problem! Just in case here is scala version of the code above.Sherris
P
5

This is already supported by jackson

objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
Preciosity answered 16/12, 2016 at 2:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.