2

I'm using JsonReader to parse some JSON data. It's a big array of elements of similar structure:

[  
   {  
      "type":"duel",
      "discipline":"leagueoflegends",
      "tournament_id":"57ee13fe140ba0cd2a8b4593",
      "opponents":[  
         {  
            "participant":null,
            "forfeit":false,
            "number":1,
            "result":1,
            "score":3
         },
         {  
            "participant":null,
            "forfeit":false,
            "number":2,
            "result":3,
            "score":2
         }
      ],
      "id":"58078b2770cb49c45b8b45bf",
      "status":"completed",
      "number":1,
      "stage_number":1,
      "group_number":1,
      "round_number":1,
      "date":"2016-10-01T05:00:00+0300",
      "timezone":"Europe/Helsinki",
      "match_format":null
   },
   ...
]

There's hundreds of similar elements in this array. I only need some data from each element and I wrote code similar to this:

public List<Match> readJsonStream(InputStream in) throws IOException {
    JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
    try {
        return readMatchesArray(reader);
    } finally {
        reader.close();
    }
}

public List<Match> readMatchesArray(JsonReader reader) throws IOException {
    List<Match> matches = new ArrayList<>();

    reader.beginArray();
    while (reader.hasNext()) {
        matches.add(readMatch(reader));
    }
    reader.endArray();
    return matches;
}

public Match readMatch(JsonReader reader) throws IOException {
    String status = null, date = null, time = null;
    Match.Bracket bracket = null;
    int id = -1, round = -1;
    Match.Team team1 = null, team2 = null;

    reader.beginObject();
    while (reader.hasNext()) {
        String name = reader.nextName();
        switch (name) {
            case "number":
                id = reader.nextInt();
                break;
            case "status":
                status = reader.nextString().toUpperCase();
                break;
            case "group_number":
                if (reader.nextInt() == 1) {
                    bracket = WINNERS;
                } else if (reader.nextInt() == 2) {
                    bracket = LOSERS;
                }
                break;
            case "round_number":
                round = reader.nextInt();
                break;
            case "date":
                try {
                    String tempDateRaw = reader.nextString();
                    String[] tempDate = tempDateRaw.split("T");
                    String[] tempTime = tempDate[1].split(":");

                    date = tempDate[0];
                    time = tempTime[0] + ":" + tempTime[1];
                } catch (IllegalStateException e) {
                    date = null;
                    time = null;
                }
                break;
            case "opponents":
                int counter = 0;
                reader.beginArray();
                while (reader.hasNext()) {
                    if (counter == 0) {
                        team1 = readTeam(reader);
                    } else {
                        team2 = readTeam(reader);
                    }
                    counter++;
                }
                reader.endArray();
                break;
            default:
                reader.skipValue();
                break;
        }
    }
    reader.endObject();
    return new Match(team1, team2, id, round, bracket, status, date, time);
}


public Match.Team readTeam(JsonReader reader) throws IOException {
    String teamName = null;
    int score = -1;
    boolean winner = false;

    reader.beginObject();
    while (reader.hasNext()) {
            String name = reader.nextName(); //this is where the error occurs
            switch (name) {
                case "participant":
                    if(reader.peek() != JsonToken.NULL) {
                        teamName = reader.nextString();
                    }
                    else {
                        teamName = null;
                    }
                    break;
                case "score":
                    if(reader.peek() != JsonToken.NULL) {
                        score = reader.nextInt();
                    }
                    else {
                        score = -1;
                    }
                    break;
                case "result":
                    if(reader.peek() != JsonToken.NULL) {
                        winner = reader.nextInt() == 1;
                    }
                    else {
                        winner = false;
                    }
                    break;
                default:
                    reader.skipValue();
                    break;
            }
    }
    reader.endObject();

    return new Match.Team(teamName, score, winner);
}

However, when I'm trying to parse the opponents array I have an error in readTeam() Expected a name but was NULL and I'm very confused why is this happening.

At first I didn't know about the peek() so in readMatch() method I used try/catch, I have to change that, but it shouldn't be relevant to the problem. I couldn't find anything about this particular error, there's quite a lot topics regarding other, similar errors (Expected name is string/int/whatever) but here I can't quite find the reason why it's not working. Do you have any ideas how to fix it?

3
  • Is there a reason you are not using GSON to parse the JSON? Commented Nov 6, 2016 at 17:13
  • Well, not really. I'm still beginner in Java and a complete newb in Android, and that was the first thing that I saw regarding parsing JSON. Previously I simply used StringBuilder and used getJSONObject(), but because the file is so big I ran out of memory. I guess I could try GSON, but first of all I don't have too much time and second of all, I'm quite confident that my problem is easily solvable, I just can't find the cause. Commented Nov 6, 2016 at 17:22
  • Do have a look at GSON when you find the time. Good luck. Commented Nov 6, 2016 at 17:26

2 Answers 2

4

You should consume null tokens, too, calling nextNull() or skipValue(), otherwise Reader won't advance and will stick with this null. For example:

case "result":
    if(reader.peek() != JsonToken.NULL) {
        winner = reader.nextInt() == 1;
    } else {
        winner = false;
        reader.nextNull(); // note on this
    }
    break;

https://developer.android.com/reference/android/util/JsonReader

Null literals can be consumed using either nextNull() or skipValue().

Sign up to request clarification or add additional context in comments.

Comments

1

Alright, I found a solution, just like I thought, it was simpler than I expected. So basically when I'm checking for null values, like this:

 if(reader.peek() != JsonToken.NULL) {
   teamName = reader.nextString();
 }
 else {
   teamName = null;
 }

the so called "reader cursor" doesn't move, so it's basically stuck on the value of the teamName (in this case). So the simplest solution is to just put reader.skipValue(); in else block. I also found another bug in my code, in case "group_number" where I actually move the aforementioned "cursor" twice, but the solution is obvious. Hopefully it will be helpful for someone.

1 Comment

This will teach me to not read through all the answers initially :). This was exactly the problem I had. I used nextNull() instead of skipValue(), but it doesn't look like it matters in my situation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.