Android JsonReader can't iterate json array

dumbfingers picture dumbfingers · Apr 8, 2013 · Viewed 8.1k times · Source

I'm playing with JsonReader which is a streaming JSON parser in Android, and I met a problem that the parser won't go over all the items in an json array, it will only return me the first item.

Here's the code I use to do the parsing:

private ArrayList<Result> readResultsArray(JsonReader reader) throws IOException {
    ArrayList<Result> results = new ArrayList<Result>();

    reader.beginObject();
    while (reader.hasNext()) {
        String name = reader.nextName();
        if (name.equals("results")){
            reader.beginArray();
            while (reader.hasNext()) {
                results.add(readResult(reader));
            }
            reader.endArray();
        } else {
            reader.skipValue();
        }
    }
    reader.endObject();

    return results;
}

I'm sure that the method readResult(reader) would parse and return the first object in the array without any problem.

But the reader seemed just "ignore" the second object in the array.

Here's the readResult(reader) method:

    private Result readResult(JsonReader reader) throws IOException {

        String from_user = null, 
                from_user_id_str = null, 
                from_user_name = null,
                text = null;
        Geo geo = null;
        URL profile_image_url = null;

        reader.beginObject();
        while (reader.hasNext()) {
            String name = reader.nextName();
            if (name.equals("from_user")){

                from_user = reader.nextString();
                Log.i(TAG, from_user);
            } else if (name.equals("from_user_id_str")) {

                from_user_id_str = reader.nextString();
                Log.i(TAG, from_user_id_str);
            } else if (name.equals("from_user_name")) {

                from_user_name = reader.nextString();
                Log.i(TAG, from_user_name);
            } else if (name.equals("geo")) {

                reader.beginObject();

                while(reader.hasNext()) {
//                      String s = reader.nextName();
                        if (name.equals("coordinates")) {
                            reader.beginArray();
                            double latitude = 0;
                            double longitude = 0;
                            while (reader.hasNext()) {
                                latitude = reader.nextDouble();
                                longitude = reader.nextDouble();
                                Log.i(TAG, latitude + ", " + longitude);
                            geo = new Geo(latitude, longitude);
                        }
                        reader.endArray();
                    } else {
                        reader.skipValue();
                    }
                }
                reader.endObject();

            } else if (name.equals("profile_image_url")) {

                profile_image_url = new URL(reader.nextString());

            } else if (name.equals("text")) {

                text = reader.nextString();
                Log.i(TAG, text);
            } else {

                reader.skipValue();

            }

        }
        reader.endObject();


        return new Result(from_user, from_user_id_str, from_user_name, text, geo, profile_image_url);
    }

}

Here's the JSON I tried to parse:

(It is a Twitter search response, the original response is presented like below, Link)

{
  "completed_in": 0.067,
  "max_id": 320160565840457730,
  "max_id_str": "320160565840457730",
  "page": 1,
  "query": "%E5%90%83",
  "refresh_url": "?since_id=320160565840457730&q=%E5%90%83&geocode=51.483372%2C-3.190826%2C200mi",
  "results": [
    {
      "created_at": "Fri, 05 Apr 2013 13:06:41 +0000",
      "from_user": "missmanchu",
      "from_user_id": 359296258,
      "from_user_id_str": "359296258",
      "from_user_name": "miss manchu ",
      "geo": null,
      "location": "London, United Kingdom",
      "id": 320160565840457730,
      "id_str": "320160565840457730",
      "iso_language_code": "ja",
      "metadata": {
        "result_type": "recent"
      },
      "profile_image_url": "http://a0.twimg.com/profile_images/3470783234/6ae4049d90a052c7a9c7f86ec804e523_normal.jpeg",
      "profile_image_url_https": "https://si0.twimg.com/profile_images/3470783234/6ae4049d90a052c7a9c7f86ec804e523_normal.jpeg",
      "source": "&lt;a href=&quot;http://twitter.com/download/iphone&quot;&gt;Twitter for iPhone&lt;/a&gt;",
      "text": "RT @lougirlie: Delish Taiwanese-style bao by @missmanchu for lunch. 好吃! http://t.co/clRznskvqe"
    },
    {
      "created_at": "Fri, 05 Apr 2013 11:45:09 +0000",
      "from_user": "lougirlie",
      "from_user_id": 154974289,
      "from_user_id_str": "154974289",
      "from_user_name": "Louise Chow",
      "geo": {
        "coordinates": [
          51.5216788595,
          -0.1420583511
        ],
        "type": "Point"
      },
      "id": 320140044142772224,
      "id_str": "320140044142772224",
      "iso_language_code": "ja",
      "metadata": {
        "result_type": "recent"
      },
      "place": {
        "full_name": "Westminster",
        "id": "548c2b8e3921a85a",
        "type": "CITY"
      },
      "profile_image_url": "http://a0.twimg.com/profile_images/1750994903/photo_3__4__normal.JPG",
      "profile_image_url_https": "https://si0.twimg.com/profile_images/1750994903/photo_3__4__normal.JPG",
      "source": "&lt;a href=&quot;http://twitter.com&quot;&gt;Twitter for  iPhone&lt;/a&gt;",
      "text": "Delish Taiwanese-style bao by @missmanchu for lunch. 好吃! http://t.co/clRznskvqe"
    },
    {
      "created_at": "Fri, 05 Apr 2013 10:09:25 +0000",
      "from_user": "geleikai",
      "from_user_id": 351512769,
      "from_user_id_str": "351512769",
      "from_user_name": "geleikai",
      "geo": null,
      "location": "53.762824,-2.707418",
      "id": 320115951339200512,
      "id_str": "320115951339200512",
      "iso_language_code": "zh",
      "metadata": {
        "result_type": "recent"
      },
      "profile_image_url": "http://a0.twimg.com/profile_images/3110335584/545b94d39e1628d747428c73a8052bf8_normal.jpeg",
      "profile_image_url_https": "https://si0.twimg.com/profile_images/3110335584/545b94d39e1628d747428c73a8052bf8_normal.jpeg",
      "source": "&lt;a href=&quot;http://www.wechatapp.com&quot;&gt;WeChat App&lt;/a&gt;",
      "text": "吃过午饭,前往阿姆斯特丹 http://t.co/LbQ0Jh7Mfp"
    },
    {
      "created_at": "Thu, 04 Apr 2013 10:15:05 +0000",
      "from_user": "hpfly",
      "from_user_id": 60939059,
      "from_user_id_str": "60939059",
      "from_user_name": "0C哈根达斯",
      "geo": null,
      "location": "53.805042,-1.544011",
      "id": 319754991692824576,
      "id_str": "319754991692824576",
      "iso_language_code": "zh",
      "metadata": {
        "result_type": "recent"
      },
      "profile_image_url": "http://a0.twimg.com/profile_images/3251267049/c56ca4cb888736299548e5454a9d923b_normal.jpeg",
      "profile_image_url_https": "https://si0.twimg.com/profile_images/3251267049/c56ca4cb888736299548e5454a9d923b_normal.jpeg",
      "source": "&lt;a href=&quot;http://fanfou.com&quot;&gt;fanfou2.0&lt;/a&gt;",
      "text": "现在对谈恋爱一点欲望都没有,更多的欲望是告诉我,我内心希望独享一个人的时光。现在特别希望晚睡,而且是晚上一个人吃着宵夜,看着电视。#如果生活可以这么单纯就好了##当然这也是晚睡的牵强理由#"
    },
    {
      "created_at": "Wed, 03 Apr 2013 15:47:50 +0000",
      "from_user": "cushingliu",
      "from_user_id": 87816289,
      "from_user_id_str": "87816289",
      "from_user_name": "Cushing",
      "geo": {
        "coordinates": [
          0.0,
          0.0
        ],
        "type": "Point"
      },
      "id": 319476345325359104,
      "id_str": "319476345325359104",
      "iso_language_code": "zh",
      "metadata": {
        "result_type": "recent"
      },
      "place": {
        "full_name": "Batu Ampar",
        "id": "ddc693317ed3f226",
        "type": "CITY"
      },
      "profile_image_url": "http://a0.twimg.com/profile_images/3269471232/f89db3764b70bada908ab340a9fb9c85_normal.jpeg",
      "profile_image_url_https": "https://si0.twimg.com/profile_images/3269471232/f89db3764b70bada908ab340a9fb9c85_normal.jpeg",
      "source": "&lt;a href=&quot;http://tapbots.com/tweetbot&quot;&gt;Tweetbot for iOS&lt;/a&gt;",
      "text": "有妈妈做饭吃的感觉真好"
    }
  ],
  "results_per_page": 15,
  "since_id": 0,
  "since_id_str": "0"
}

The only part I'm interested in is "results", which is an array of two objects.

After took a close look at the example in Android Developers - JsonReader as well as this nice tutorial, I found mine is basically no difference with theirs.

So what's wrong with my code? Why it cannot itinerate the json array?

UPDATE

Pasted the code of readResult(reader)

Answer

Deepak Bala picture Deepak Bala · Apr 8, 2013

You are not accounting for null geo types which will cause an exception on the original code. Use peek() to check for null. I've modified the code to handle this and it prints both objects.

class MyReader
{
    public void readBegin(JsonReader reader)
    {
        try
        {
            reader.beginObject();
            while (reader.hasNext())
            {
                String name = reader.nextName();
                if (name.equals("results"))
                {
                    reader.beginArray();
                    while (reader.hasNext())
                    {
                        read(reader);
                    }
                    reader.endArray();
                }
                else
                {
                    reader.skipValue();
                }
            }
            reader.endObject();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public void read(JsonReader reader) throws Exception
    {

        String from_user = null, from_user_id_str = null, from_user_name = null, text = null;
        URL profile_image_url = null;

        reader.beginObject();
        while (reader.hasNext())
        {
            String name = reader.nextName();
            if (name.equals("from_user"))
            {
                from_user = reader.nextString();
                System.out.println(from_user);
            }
            else if (name.equals("from_user_id_str"))
            {

                from_user_id_str = reader.nextString();
            }
            else if (name.equals("from_user_name"))
            {

                from_user_name = reader.nextString();
            }
            else if (name.equals("geo"))
            {
                if (reader.hasNext())
                {
                    JsonToken peek = reader.peek();
                    if (peek == JsonToken.NULL)
                    {
                        reader.skipValue();
                    }
                    else
                    {
                        reader.beginObject();
                        while (reader.hasNext())
                        {
                            // String s = reader.nextName();
                            if (name.equals("coordinates"))
                            {
                                reader.beginArray();
                                double latitude = 0;
                                double longitude = 0;
                                while (reader.hasNext())
                                {
                                    latitude = reader.nextDouble();
                                    longitude = reader.nextDouble();
                                }
                                reader.endArray();
                            }
                            else
                            {
                                reader.skipValue();
                            }
                        }
                        reader.endObject();
                    }
                }

            }
            else if (name.equals("profile_image_url"))
            {

                profile_image_url = new URL(reader.nextString());

            }
            else if (name.equals("text"))
            {

                text = reader.nextString();
            }
            else
            {
                reader.skipValue();
            }
        }
        reader.endObject();
    }