Blaze Tama
Blaze Tama

Reputation: 10948

gson read same field with different structure

Note : This can be achieved easily by creating 2 different GSON models, but i'm looking for a workaround.

I have a model that work with multiple JSON responses. However there is a response :

items: [
{
kind: "youtube#playlistItem",
etag: ""fpJ9onbY0Rl_LqYLG6rOCJ9h9N8/jqbcTLu8tYm8b1FXGO14gNZrFG4"",
id: "PLUQ7I1jJqKB4lJarGcpWsP62l7iC06IkE2LDE0BxLe18",

That conflict with this one (notice the same id field with different type. The above id is String, the other is a Class) :

items: [
{
kind: "youtube#searchResult",
etag: ""fpJ9onbY0Rl_LqYLG6rOCJ9h9N8/hDIU49vmD5aPhKUN5Yz9gtljG9A"",
id: {
kind: "youtube#playlist",
playlistId: "PLh6xqIvLQJSf3ynKVEc1axUb1dQwvGWfO"
},

Is it possible to just use a single GSON model that can read both responses?

Thanks a lot for your time

UPDATE

This is inside my Model, i changed the id variable from String to a Class :

private Id id;
public class Id
{
    private String id;
    private String kind;
    private String playlistId;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getKind() {
        return kind;
    }

    public void setKind(String kind) {
        this.kind = kind;
    }

    public String getPlaylistId() {
        return playlistId;
    }

    public void setPlaylistId(String playlistId) {
        this.playlistId = playlistId;
    }
}

This is how i do the request :

        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(Response.class,
                new JsonDeserializer<Item.Id>() {
                    @Override
                    public Item.Id deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
                                                    if(jsonElement.isJsonObject())
                        {
                            result.setKind(jsonElement.getAsJsonObject().get("kind").getAsString());
                            result.setPlaylistId(jsonElement.getAsJsonObject().get("playlistId").getAsString());
                            //return new Item.Id(jsonElement.getAsJsonObject().get("kind").getAsString(), jsonElement.getAsJsonObject().get("playlistId").getAsString());
                            return result;
                        }
                        else
                        {
                            result.setId(jsonElement.getAsString());
                            //return new Item.Id(jsonElement.getAsString());
                            return result;
                        }
                    }
                });

Upvotes: 0

Views: 802

Answers (1)

Pasi Matalam&#228;ki
Pasi Matalam&#228;ki

Reputation: 1853

The problem

You can only have id represent single type of variable, in this case on the first response you've got string, and on second case you've got an object, which has a "kind" and a "playlistId"

This seems like a weird design on backend side, but it probably isn't under your control

Solution

What you can do is if you think that the id in the first, and second responses are of same type you could create an encapsulating class that would contain these fields like

class Id {
    private String stringId;
    private String kind;
    private String playlistId;
}

and create a custom JsonDeserializer that would based on the type of the JSON response create a corresponding Id object in following fashion:

new JsonDeserializer<Id>() {
            @Override
            public Id deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
                if(json.isJsonObject()) {
                    return new Id(json.getAsJsonObject().get("kind").getAsString(), json.getAsJsonObject().get("playlistId").getAsString());
                } else {
                    return new Id(json.getAsString());
                }
            }
        }

and register it on your gson object, so it would handle the deserialization of Id fields, and depending of its type base its creation

Or then you could create a deserializer that would deserialize the containing object and use different fields based on the type of id, but you should get the idea!

You can register your JsonDeserializer on gson using GsonBuilder and in GsonBuilder calling registerTypeAdapter with the type you're registering type adapter on and with the typeadapter you want to use in the following way:

return new GsonBuilder().registerTypeAdapter(Id.class, new JsonDeserializer<Id>() {

    //..

}).build();

You can read more about custom typeAdapters in gson documentation, here!

Upvotes: 2

Related Questions