Monday, December 14, 2009

Handling JSON arrays and objects

Recently I was using the Apache Wink client-side capabilities to interface with the RESTful APIs of several popular social media sites like Twitter and bit.ly, and I ran into an interesting problem regarding handling the JSON data being returned from those services. Since I think quite a few people may hit this same issue, I thought it may be helpful to share one way of dealing with the problem.

What is the problem? Well, it turns out that in dealing with these services they were sending back JSON in two different forms. One of those forms was the object form of JSON, which looks like the following:

{"name":"Dustin", "userid":"WebSphereClouds"}

The other form that was getting sent back was the array form of JSON, which starts with a [ instead of a {:

[{"name":"Dustin", "userid":"WebSphereClouds"}]

That may not seem like a big deal, but it does in fact affect the way the data is parsed and the Java object type to which the data is converted.

For instance, consider my application which uses the IBM JSON4J library. If I were trying to convert a response input stream containing data in JSON object form, I would call JSONObject.parse(responseInputStream) and that would return a JSONObject. On the other hand, if I were converting a response input stream containing data in JSON array form I would call JSONArray.parse(responseInputStream) and that would return a JSONArray. Clearly, my application needed a way to determine what form of JSON data it was handling.

To do this, I created a simple class that encapsulates this data type determination. It contains two methods to parse a response input stream and return a JSONArtifact type which is an interface implemented by both JSONObject and JSONArray.

private enum JSONType {
ARRAY_TYPE,
REGULAR_TYPE
}
public JSONArtifact parse(InputStream is) throws IOException {
JSONArtifact parsedJSON = null;
FilterInputStream fis = new BufferedInputStream(is);
JSONType jsonType = getJSONType(fis);
if(jsonType == JSONType.ARRAY_TYPE) {
parsedJSON = JSONArray.parse(fis);
}
else {
parsedJSON = JSONObject.parse(fis);
}
return parsedJSON;
}
JSONType getJSONType(FilterInputStream fis) throws IOException {
JSONType type = null;
fis.mark(1);
byte[] bytes = new byte[1];
fis.read(bytes);
fis.reset();
String firstChar = new String(bytes);
if("[".equals(firstChar)) {
type = JSONType.ARRAY_TYPE;
}
else {
type = JSONType.REGULAR_TYPE;
}
return type;
}

The class simply reads the first byte of data from the stream, resets the stream, and then determines, based on whether the first byte is { or [, whether to parse using the JSONArray or JSONObject class.

I also want to point out what I would consider to be a best practice with respect to JSON data sent back from a RESTful service. When developing RESTful services on the server-side, I suggest sending back the object form of JSON. If necessary, you can encapsulate arrays in this object form:

{"userInfo":[{"name":"Dustin", "userid":"WebSphereClouds"}]}

This will make for simpler parsing by clients of your services, and you can easily add data to what you return by adding more entries in the JSON without regressing existing clients.

7 comments:

  1. I got asked if this could also be done to support non ASCII/UTF-8 content. The answer is of course it can. The JSONArray and JSONObject parse methods are overloaded to offer a version that accepts a Reader. One could easily create an InputStreamReader and declare the encoding type. In that sense, the sample parser I show above should also overload the parse method to offer a version that accepts a Reader type.

    ReplyDelete
  2. You should look at EcmaScript 5's JSON.stringify() and parse methods. They both deal with arbitrary JavaScript objects, including numbers, strings, arrays, literal objects, etc. Might be worthwhile looking into whether the JSON4J libraries could handle such cases.

    ReplyDelete
  3. I'm assuming you are curious about whether JSON4J can handle the JSON produced by JSON.stringify() and send content that can be handled by JSON.parse(). If that is in fact the question, the answer should be yes on both accounts. JSON4J just expects the JSON sent over the wire to be valid JSON content, meaning an array (start with '[') or an object ('{'). As far as sending compatible content, JSON4J will only serialize either arrays or objects. It looks like JSON.parse() will handle that type of text just fine.

    ReplyDelete
  4. To have better idea about the same just click on the link given hereby at Website Development

    ReplyDelete
  5. Very nice post! It is just what i am looking for. Thank you for sharing informative blog.
    Web Development Company

    ReplyDelete
  6. Siebel SmartScript accelerates abettor capability with a workflow-based, dynamically generated user interface that helps adviser every alternation with a customer. Siebel SmartScript can be invoked automatically several ways, such as based aloft business rules, through awning pops, by Siebel CTI, or programmatically.

    call center outsourcing

    ReplyDelete
  7. You can information about internet marketing that are published on reputed sites. He has a very nice understanding about Web development company delhi 

    ReplyDelete