Working with Android’s In-memory JSONObject

By Rob Gravelle

Working with Android’s In-memory JSONObject

These days, JSON is considered to be a superior data exchange format to XML due to its lighter weight. As it happens, Android provides several specialized classes for parsing JSON. These have been around ever since API level 1 (the first Android release)! In today's article, we'll learn how to use Android's JSON classes to load a JSON data set into memory and work with its contents.

Step One: Fetching the Data

Although it is possible to load JSON data from a file, in most cases, your app will fetch the data from a feed or other online server, for instance, a weather office or Bugzilla's REST API.

We'll be working with the latter for the remainder of this article. Here is the formatted JSON string for bug #35 at "https://bugzilla.mozilla.org/rest/bug/35":

{
   "bugs" : [
      {
         "alias" : "firstBug",
         "assigned_to" : "mcafee@gmail.com",
         "assigned_to_detail" : {
            "email" : "mcafee@gmail.com",
            "id" : 1672,
            "name" : "mcafee@gmail.com",
            "real_name" : "Chris McAfee"
         },
         "blocks" : [],
         "cc" : [
            "hchang@mozilla.com",
            "rexyrexy2@gmail.com",
            "tymerkaev@gmail.com",
            "wlevine@gmail.com"
         ],
         "cc_detail" : [
            {
               "email" : "hchang@mozilla.com",
               "id" : 475800,
               "name" : "hchang@mozilla.com",
               "real_name" : "Henry Chang [:henry][:hchang]"
            },
            {
               "email" : "rexyrexy2@gmail.com",
               "id" : 463956,
               "name" : "rexyrexy2@gmail.com",
               "real_name" : ""
            },
            {
               "email" : "tymerkaev@gmail.com",
               "id" : 356256,
               "name" : "tymerkaev@gmail.com",
               "real_name" : ""
            },
            {
               "email" : "wlevine@gmail.com",
               "id" : 68465,
               "name" : "wlevine@gmail.com",
               "real_name" : "Will Levine"
            }
         ],
         "cf_fx_iteration" : "---",
         "cf_fx_points" : "---",
         "cf_last_resolved" : "1998-12-12T17:06:46Z",
         "cf_qa_whiteboard" : "",
         "cf_user_story" : "",
         "classification" : "Graveyard",
         "component" : "XFE",
         "creation_time" : "1998-04-07T08:37:03Z",
         "creator" : "weitsang@cs.cornell.edu",
         "creator_detail" : {
            "email" : "weitsang@cs.cornell.edu",
            "id" : 55,
            "name" : "weitsang@cs.cornell.edu",
            "real_name" : ""
         },
         "depends_on" : [],
         "dupe_of" : null,
         "flags" : [],
         "groups" : [],
         "id" : 35,
         "is_cc_accessible" : true,
         "is_confirmed" : true,
         "is_creator_accessible" : true,
         "is_open" : false,
         "keywords" : [],
         "last_change_time" : "2013-11-20T02:16:47Z",
         "mentors" : [],
         "mentors_detail" : [],
         "op_sys" : "Solaris",
         "platform" : "Sun",
         "priority" : "P3",
         "product" : "MozillaClassic",
         "qa_contact" : "",
         "resolution" : "WONTFIX",
         "see_also" : [],
         "severity" : "minor",
         "status" : "VERIFIED",
         "summary" : "Navigator does not free preference hash table when exit.",
         "target_milestone" : "---",
         "url" : "",
         "version" : "1998-03-31",
         "votes" : 0,
         "whiteboard" : ""
      }
   ],
   "faults" : []
}

Fetching the Data

Reading from an input stream can be accomplished in a variety of ways. Here's some code that wraps the InputStreamin within a BufferedInputStream so that the data may be read one line at a time. This is accomplished by appending each line to a StringBuilder and then converting it into a String once the entire file has been read.

HttpURLConnection urlConnection;
StringBuilder result = new StringBuilder();
try {
    URL url        = new URL("https://bugzilla.mozilla.org/rest/bug/35");
    urlConnection  = (HttpURLConnection) url.openConnection();
    InputStream in = new BufferedInputStream(urlConnection.getInputStream());

    BufferedReader reader = new BufferedReader(new InputStreamReader(in));

    String line;
    while ((line = reader.readLine()) != null) {
        result.append(line);
    }
    String input = result.toString();
    //processing code will go here...
}catch( Exception e) {
    e.printStackTrace();
}
finally {
    urlConnection.disconnect();
}

Step 2: Processing the JSONObject

The Bugzilla feed returns a serialized JSON object with two keys: "bugs" and "faults". Once the data is converted into a JSONObject, we can reference the "bugs" key by name to fetch its JSONArray of bug objects. In this case, the element contains only one element for bug #35. Processing of the array is handled by the iterateOverJsonArray() function because it will be required again further down the line.

JSONObject jo  = new JSONObject(input);
JSONArray bugs = jo.getJSONArray("bugs");
iterateOverJsonArray(bugs);

Inside the iterateOverJsonArray() function, a for object loop iterates over each element (again, one element in this instance), and checks whether the current attribute is a JSONObject or a native type such as a number or string.

private static void iterateOverJsonArray(JSONArray ja) {
  for (Object o: ja) {
         if ( o instanceof JSONObject ) {
                 iterateOverJsonItems( (JSONObject)o );
         }
         else {
                 System.out.println(o);
         }
  }
}

The iterateOverJsonItems() function is where the real work takes place. The JSONObject has a keys() function that returns an Iterator for, you guessed it, iterating over each key. Within the while loop, we can check each value's type using the instanceof operator. If the object is a nested JSONArray or JSONObject, it is passed on to the relevant function for further processing; otherwise, the current value is outputted to the console. Notice the use of casting to get at the underlying object type.

private static void iterateOverJsonItems( JSONObject jo ) {
  Iterator<String> keys = jo.keys();

  while( keys.hasNext() ) {
    String key = (String)keys.next();
    Object value = jo.get(key);
    System.out.print(key + ": ");
    if ( value instanceof JSONArray ) {
      System.out.print("[");
      if ( ((JSONArray) value).length() > 0 ) {
        System.out.print("\n");
        iterateOverJsonArray( (JSONArray)value );
        System.out.println("]");
      }
      else {
        System.out.print("]\n");
      }
    }
    else if ( value instanceof JSONObject ) {
      System.out.println("{");
      iterateOverJsonItems( (JSONObject)value );
      System.out.println("}");
    }
    else {
      System.out.println(value);
    }
  }
}

Here is a portion of the program output. The exact format is not as important as the exercise of utilizing the JSONObject and JSONArray classes to work with the data.

creation_time: 1998-04-07T08:37:03Z
keywords: []
is_cc_accessible: true
flags: []
whiteboard:
resolution: WONTFIX
see_also: []
platform: Sun
cf_fx_points: ---
cf_fx_iteration: ---
alias: firstBug
id: 35
assigned_to: mcafee@gmail.com
dupe_of: null
cc: [
hchang@mozilla.com
rexyrexy2@gmail.com
tymerkaev@gmail.com
wlevine@gmail.com
]

Conclusion

Although Android's JSONObject and JSONArray classes make it easy to extract the bits of the data that we need, they also require the entire object to be read into a String first. For a very large data set, this will consume a lot of memory. In fact, attempting to input too large a dataset can cause a java.lang.OutOfMemoryError. Several third-party parsers have come about to deal with this problem. We'll take a look at some of these in future articles.



Rob Gravelle

Rob Gravelle resides in Ottawa, Canada, and is the founder of GravelleWebDesign.com. Rob has built web applications for numerous businesses and has recently developed his own jquery-tables library.

Rob's alter-ego, "Blackjacques", is an accomplished guitar player, that has released several CDs. His band, Ivory Knight, was rated as one of Canada's top hard rock and metal groups by Brave Words magazine (issue #92) and reached the #1 spot in the National Heavy Metal charts on ReverbNation.com.

Rob uses and recommends MochaHost, which provides Web Hosting for as low as $1.95 per month, unlimited emails, and unlimited disk space!



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •  
  •  
  •  
Thanks for your registration, follow us on our social networks to keep up-to-date