1

I have a model that work with multiple JSON responses. However this 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"
},

I want to read the id field using a single model class.

This is my model Response class :

public class Response {
    private ArrayList<Item> items = new ArrayList<Item>();

And this is my model Item class :

public class Item {
    private Id id;

//nested class inside Item
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;
    }
}

Notice that the Id class is inside Item class.

This is how i use registerTypeAdapter :

 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 {
                            Item.Id result = new Item().new Id();

                            if(jsonElement.isJsonPrimitive() == false)
                            {
                                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;
                            }
                        }
                    });
            Gson gson = gsonBuilder.create();

            Response result = Response.success(
                    gson.fromJson(json, gsonClass),
                    HttpHeaderParser.parseCacheHeaders(response));

However the above code throws java.lang.NullPointerException in this line :

if(jsonElement.isJsonPrimitive() == false)

What should i do? Is this the correct way to use registerTypeAdapter?

Thanks for your time

Blaze Tama
  • 10,828
  • 13
  • 69
  • 129

2 Answers2

3

Your main problem seems to be that you're registering a type adapter for Response class but you're giving a JsonDeserializer that handles Item.Id classes. Either you deserialize a full Response or limit yourself to Item or even Item.Id.

Another problem with deserializing Item.Id (as written in your question) directly is that it's a non-static inner class, which requires an instance of the parent class to be instantiated (as you do with the new Item().new Id()). I think that you would have to deserialize the whole Item manually if you want to keep it as such, frankly I don't see any reason not to make Item.Id static as it would simplify the problem.

Here's my solution with the static version of Item.Id

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Item.Id.class, new JsonDeserializer<Item.Id>() {
    @Override
    public Item.Id deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Item.Id id = new Item.Id();
        if (!json.isJsonPrimitive()) {
            JsonObject jsonObject = json.getAsJsonObject();
            id.setKind(jsonObject.get("kind").getAsString());
            id.setPlaylistId(jsonObject.get("playlistId").getAsString());
        } else {
            id.setId(json.getAsString());
        }
        return id;
    }
});

And some test snippets that seemed to work for me:

Gson gson = builder.create();
Response response = gson.fromJson("{\n" +
        "    \"items\": [\n" +
        "        {\n" +
        "            \"kind\": \"youtube#playlistItem\",\n" +
        "            \"etag\": \"fpJ9onbY0Rl_LqYLG6rOCJ9h9N8/jqbcTLu8tYm8b1FXGO14gNZrFG4\",\n" +
        "            \"id\": \"PLUQ7I1jJqKB4lJarGcpWsP62l7iC06IkE2LDE0BxLe18\"\n" +
        "        }\n" +
        "    ]\n" +
        "}", Response.class);

Response response2 = gson.fromJson("{\n" +
        "    \"items\": [\n" +
        "        {\n" +
        "            \"kind\": \"youtube#searchResult\",\n" +
        "            \"etag\": \"fpJ9onbY0Rl_LqYLG6rOCJ9h9N8/hDIU49vmD5aPhKUN5Yz9gtljG9A\",\n" +
        "            \"id\": {\n" +
        "                \"kind\": \"youtube#playlist\",\n" +
        "                \"playlistId\": \"PLh6xqIvLQJSf3ynKVEc1axUb1dQwvGWfO\"\n" +
        "            }\n" +
        "        }\n" +
        "    ]\n" +
        "}", Response.class);

If you want to keep Item.Id non-static, I think you need to write a deserializer for Item instead. Also keep in mind that json elements can be absent but the parser should still be able to handle it.

Zharf
  • 2,638
  • 25
  • 26
  • Thank you very much for your effort to write this answer. Its too bad i can only upvote once. Hope someone else will upvote this great answer. Thanks again :) – Blaze Tama Oct 16 '15 at 03:32
  • If i may add a question, why do i need to deserialize the whole `Item` if i dont make the inner `Id` class static? – Blaze Tama Oct 16 '15 at 03:34
  • 1
    @BlazeTama I'm not 100% sure on that, actually, but instances of non-static inner classes have a reference to their parent, so I would imagine that you'd need that reference in order to create an instance of it... I'm not sure if there's some magic underneath in java that changes that reference to another parent class instance when you assign it to another instance of the parent (I think not). If it works like I think it does, you'd end up with Id that refers internally to some other Item instance. – Zharf Oct 16 '15 at 08:38
1

I had just the same problem long time ago, but i was working then with Json.Net, However json concept is (almost) the same so I hope I will succeed help. I guess it might not work, but why try to not register with the Item class? I mean there is no 'Item.Id' in so it's obious get a null. BUT this: gsonBuilder.registerTypeAdapter(Item.class, likely to fail, becaus you dont parse the Item class explicitly (However it might work, I just never tried it in Gson. But in Json.Net similar way would work proper.) So, how could you fix that? I'm too lazy to write an paraser but you could look here and read the surt summary summary from here (Search for Using the registerTypeAdapter(). I hope I helped.

Community
  • 1
  • 1
Remy
  • 1,053
  • 1
  • 19
  • 25