0

Like the title says, I am trying to parse an object which key is a dynamic String. I've looked quite a while for some answers and found 1. this, 2.this and 3. this

For your information, I kinda new to Java and this is my first time working with Json data and parsing

The Json response looks like this:

{"Narcos": {"episodes": [{"season": 2, "name": "Episode #2.1", "number": 1}, {"season": 2, "name": "Episode #2.10", "number": 10}, {"season": 2, "name": "Episode #2.2", "number": 2}, {"season": 2, "name": "Episode #2.3", "number": 3}, {"season": 2, "name": "Episode #2.4", "number": 4}, {"season": 2, "name": "Episode #2.5", "number": 5}, {"season": 2, "name": "Episode #2.6", "number": 6}, {"season": 2, "name": "Episode #2.7", "number": 7}, {"season": 2, "name": "Episode #2.8", "number": 8}, {"season": 2, "name": "Episode #2.9", "number": 9}, {"season": 1, "name": "Descenso", "number": 1}, {"season": 1, "name": "Despegue", "number": 10}, {"season": 1, "name": "Explosivos", "number": 6}, {"season": 1, "name": "La Catedral", "number": 9}, {"season": 1, "name": "La Gran Mentira", "number": 8}, {"season": 1, "name": "The Men of Always", "number": 3}, {"season": 1, "name": "The Palace in Flames", "number": 4}, {"season": 1, "name": "The Sword of Sim\u00f3n Bolivar", "number": 2}, {"season": 1, "name": "There Will Be a Future", "number": 5}, {"season": 1, "name": "You Will Cry Tears of Blood", "number": 7}], "year": 2015}}

The dynamic key, is in this example "Narcos". Its basically the name of the show you want the episodes for.

I tried different approaches. For the first link, the code for parsing looks like this:

ApiTest.class

Type mapType = new  TypeToken<Map<String, Serie> >() {}.getType(); // define generic type

    Map<String, Serie> serie = gson.fromJson(jsonString, mapType);

Serie.class

 public class Serie {

    private Episodes episodes;

Episodes.class

public class Episodes {

    private Episode[] episode;
    private int year;

Episode.class

public class Episode {

    private int season;
    private String name;
    private int number;

When I go with the first approach, I get this error

Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 41 path $..episodes
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:224)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:129)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:220)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41)
    at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:187)
    at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145)
    at com.google.gson.Gson.fromJson(Gson.java:887)
    at com.google.gson.Gson.fromJson(Gson.java:852)
    at com.google.gson.Gson.fromJson(Gson.java:801)
    at testing.ApiTest.main(ApiTest.java:114)
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 41 path $..episodes
    at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:385)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:213)
    ... 9 more

When I tried to parse the data according to the other two threads, I created a response class like this:

PoroResponse.class

public class PoroResponse {

    private Map<String, Serie> serie;

and changed the code in ApiTest.class to:

PoroResponse poro = gson.fromJson(jsonString, PoroResponse.class);

and tried to get an array to work with through this:

Episode[] episodes = poro.get(poro).getSerie().getEpisodes().getEpisode();

I also tried to parse it like this:

Object o = new Gson().fromJson(json, Object.class);

But in both of the last cases either my Serie object is NULL or I get a LinkedTreeMap with NULL values.

Could someone point me in the right direction? I've been stuck at this problem since 2 days and honestly have no real idea how to fix it.

Community
  • 1
  • 1
Tagman
  • 176
  • 2
  • 11

1 Answers1

0

Maybe you can try this: Episode class

    public class Episode {

    @SerializedName("season")
    @Expose
    private Integer season;
    @SerializedName("name")
    @Expose
    private String name;
    @SerializedName("number")
    @Expose
    private Integer number;


    public Integer getSeason() {
        return season;
    }


    public void setSeason(Integer season) {
        this.season = season;
    }


    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name;
    }


    public Integer getNumber() {
        return number;
    }


    public void setNumber(Integer number) {
        this.number = number;
    }

 }

Then Serial class:

    public class Serial {

    @SerializedName("episodes")
    @Expose
    private List<Episode> episodes = new ArrayList<Episode>();
    @SerializedName("year")
    @Expose
    private Integer year;

    public List<Episode> getEpisodes() {
        return episodes;
    }


    public void setEpisodes(List<Episode> episodes) {
        this.episodes = episodes;
    }


    public Integer getYear() {
        return year;
    }


    public void setYear(Integer year) {
        this.year = year;
    }

 }

Then to generate JAVA objects with dynamic keys from JSON just run this:

Type type = new TypeToken<Map<String, Serial>>() {
        }.getType();
Map<String, Serial> result = new Gson()
                .fromJson(new InputStreamReader(new ByteArrayInputStream(jsonString.getBytes())), type);

To get for example Narcos serial information you need to write this:

Serial narcosSerial = result.get("Narcos");
uvytautas
  • 518
  • 8
  • 18
  • Thanks for the input. I should have added that Narcos is the dynamic key. It changes depending on the request to the API. So it's always the name of the show you request. This is the URL to the API, if it might help. I will edit my question to make it a bit clearer. – Tagman Sep 14 '16 at 18:13
  • Thank you so much. This is exactly what I was looking for. I am still trying to figure out why I need just these two classes. But the code works like I wanted it to. – Tagman Sep 15 '16 at 16:41