Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GSON Deserialize String or String Array

Tags:

java

json

gson

I've had some trouble deserializing an object that contains 0 to many child objects that can either contains a string or a string array for a particular value.

Here's an example JSON

{
"name": "process name",
"tasks": [{
        "name": "task 1",
        "fields": [{
                "name": "field 1",
                "value": "123abc"
            },
            {
                "name": "field 2",
                "value": ["value 1", "value 2"]
            }
        ]
    },
    {
        "name": "task 2",
        "fields": []
    }]
}

I have a Java entity setup to match this structure like this:

public class Process {
    public Process() {}

    public String name;
    public Task[] tasks;
}

public class Task {
    public Task() {}

    public String name;
    public Field[] fields;
}

public class Field {
    public Field() field;

    public String name;
    public String value;
}

And I deserialize like such:

static <T> T fetch(MyHttpRequest request, Class<T> entity)
{
    String response = sendRequestAndParse(request);
    if (response == null) {
        log.debug(String.format("API response was null %n)"));
        return null;
    }

    GsonBuilder gsonBuilder = new GsonBuilder();
    Gson gson = gsonBuilder.create();
    return gson.fromJson(response, entity);
}

I use dynamic types because there's a number of other entities other than Process that I use this same method for. But I can't figure out how to handle the case where the field value can be either a string to an array of string. Any pointers would be appreciated.

like image 805
Austin Avatar asked Oct 04 '17 15:10

Austin


1 Answers

Probably the most simple option is to use custom serializer and deserializer and change value type from String to List<String> Here is basic idea how you can solve this:

private static class MyJsonAdapter implements JsonSerializer<List<String>>,
        JsonDeserializer<List<String>>{

    @Override
    public JsonElement serialize(List<String> list, Type t,
                                 JsonSerializationContext jsc) {
        if (list.size() == 1) {
            return jsc.serialize(list.get(0));
        } else {
            return jsc.serialize(list);
        }
    }
    @Override
    public List<String> deserialize(JsonElement json, Type typeOfT,
                                    JsonDeserializationContext jsc) 
            throws JsonParseException {
        List<String> result;

        if (json.isJsonArray()) {
            result = jsc.deserialize(json, typeOfT);
        }else {
            result  =  new ArrayList<>();
            result.add((String) jsc.deserialize(json, String.class));
        }
        return result;
    }
}

And Field POJO

public static class Field {
    public String name;

    // Use @JsonAdapter(MyJsonAdapter.class)
    // or register serializer and deserializer in
    // new GsonBuilder().registerTypeAdapter(new MyJsonAdapter())
    @JsonAdapter(MyJsonAdapter.class)
    public List<String> value; // need to change value type to list
}

Ps. If you could switch to Jackson from Gson, this problem could be solved with 1 line of code DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY

like image 158
varren Avatar answered Oct 18 '22 00:10

varren