0

I am trying to deserialize a Reddit comments page using Gson, but I am getting StackOverflowError. From what I found googling, this is because of circular reference in the JSON data. The data has nested objects like below.

Listing:{
    data:List<Comment>
}
Comment:{
    ...
    replies:Listing
}

I am not sure how exactly to write a deserializer to handle this kind of data.

Can someone help me handling the data like above using Gson?

Below is one of the sample URL of the JSON data: https://www.reddit.com/r/technology/comments/6977qb/.json

Edit 1:

I have created type adapter as suggested by @Lyubomyr Shaydariv. Below is my new code.

I couldn't figure out a way to optimize the nested switch cases yet. The new code is not giving any errors but not returning any results.

public static void main(String[] args) {
    GsonBuilder gb = new GsonBuilder();
    gb.registerTypeAdapter(Thing.class, new ThingDeserializer());
    gb.registerTypeAdapter(Comment.class, new CustomAdapter());
    Gson gson = gb.create();
    try {
        List<Listing> listing = gson.fromJson(readUrlToString("https://www.reddit.com/r/technology/comments/6977qb/.json"), new TypeToken<Collection<Listing>>() {}.getType());
        System.out.println(listing);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public class Listing {
    private ListingData data;
}
public class ListingData {
    private List<Child> children = null;
}
public class Child {
    private Thing data;
}
public class Comment implements Thing {
    private Listing replies;
}

public class ThingDeserializer implements JsonDeserializer<Thing> {
    @Override
    public Thing deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json == null)
            return null;
        else {
            Thing thing =null;
            String name=json.getAsJsonObject().get("name").getAsJsonPrimitive().getAsString();
            if(name.startsWith("t1_")){
                thing=context.deserialize(json,new TypeToken<Comment>(){}.getType());
            }
            else if(name.startsWith("t2_")){
                thing=context.deserialize(json,new TypeToken<User>(){}.getType());
            }
            else if(name.startsWith("t3_")){
                thing=context.deserialize(json,new TypeToken<Post>(){}.getType());
            }
            else if(name.startsWith("t4_")){
                thing=context.deserialize(json,new TypeToken<Message>(){}.getType());
            }
            else if(name.startsWith("t5_")){
                thing=context.deserialize(json,new TypeToken<Subreddit>(){}.getType());
            }
            return thing;
        }
    }
}
public class CustomAdapter extends TypeAdapter<Comment> {
    @Override
    public Comment read(JsonReader jsonReader) throws IOException {
        Comment comment=new Comment();
        jsonReader.beginObject();
        while(jsonReader.hasNext()) {
            if (jsonReader.peek().equals(JsonToken.NAME)) {
                switch (jsonReader.nextName()) {
                    case "replies":
                        if (jsonReader.peek().equals(JsonToken.BEGIN_OBJECT)) {
                            jsonReader.beginObject();
                            Listing listing = new Listing();
                            while (jsonReader.hasNext()) {
                                switch (jsonReader.nextName()) {
                                    case "kind":
                                        listing.setKind(jsonReader.nextString());
                                        break;
                                    case "data":
                                        ListingData listingData = new ListingData();
                                        jsonReader.beginObject();
                                        while (jsonReader.hasNext()) {
                                            switch (jsonReader.nextName()) {
                                                case "children":
                                                    jsonReader.beginArray();
                                                    List<Child> childList = new ArrayList<>();
                                                    while (jsonReader.hasNext()) {
                                                        jsonReader.beginObject();
                                                        Child child = new Child();
                                                        while (jsonReader.hasNext()) {
                                                            switch (jsonReader.nextName()) {
                                                                case "data":
                                                                    child.setData(read(jsonReader));
                                                                    break;
                                                                default:
                                                                    jsonReader.skipValue();
                                                                    break;
                                                            }
                                                        }
                                                        childList.add(child);
                                                    }
                                                    listingData.setChildren(childList);
                                                    break;
                                                default:
                                                    jsonReader.skipValue();
                                                    break;
                                            }

                                        }
                                        listing.setData(listingData);
                                        break;
                                    default:
                                        jsonReader.skipValue();
                                        break;
                                }
                            }
                            comment.setReplies(listing);
                        }
                        break;
                    default:
                        jsonReader.skipValue();
                        break;
                }
            }
        }

        return comment;
    }
}
Community
  • 1
  • 1
vvincirey
  • 191
  • 3
  • 11
  • You should post a what you have so far (https://stackoverflow.com/help/mcve) so that we can see where you error is coming from. And you can refer to this question http://stackoverflow.com/questions/16590377/custom-json-deserializer-using-gson on how to write a custom deserializer, if you are not doing that already – tima May 05 '17 at 03:28
  • ~170K of JSON data to read/parse/scan by eyes, no MCVE for mappings, no exception cause stacktrace. Seriously, how? – Lyubomyr Shaydariv May 05 '17 at 06:09
  • It's even more weird that you have ran into SOE for non-recursive data format whose example data is not that too-nested and can be easily parsed at least for `JsonElement`. Are you using a custom type adapter factory (`TypeAdapterFactory`)? – Lyubomyr Shaydariv May 05 '17 at 06:16
  • adding respective jsonReader.endObject() fixed the issue. Thanks everyone for checking. – vvincirey May 09 '17 at 03:38

0 Answers0