April 8, 2013

Java API for JSON Processing (JSR-353) - Stream APIs

Java will soon have a standard set of APIs for processing JSON as part of Java EE 7.  This standard is being defined as JSR 353 - Java API for JSON Processing (JSON-P) and it is currently at the Final Approval Ballot.  JSON-P offers both object oriented and stream based approaches, in this post I will introduce the stream APIs.

You can get JSON-P reference implementation from the link below:
UPDATE

JSR-353 has passed the final approval ballot.
JsonGenerator (javax.json.stream) 

JsonGenerator makes it very easy to create JSON.  With its fluent API the code to produce the JSON very closely resembles the resulting JSON.

package blog.jsonp;

import java.util.*;
import javax.json.Json;
import javax.json.stream.*;

public class GeneratorDemo {

    public static void main(String[] args) {
        Map<String, Object> properties = new HashMap<String, Object>(1);
        properties.put(JsonGenerator.PRETTY_PRINTING, true);
        JsonGeneratorFactory jgf = Json.createGeneratorFactory(properties);
        JsonGenerator jg = jgf.createGenerator(System.out);
        
        jg.writeStartObject()                    // {
            .write("name", "Jane Doe")           //    "name":"Jane Doe",
            .writeStartObject("address")         //    "address":{
                .write("type", 1)                //        "type":1,
                .write("street", "1 A Street")   //        "street":"1 A Street",
                .writeNull("city")               //        "city":null,
                .write("verified", false)        //        "verified":false
            .writeEnd()                          //    },
            .writeStartArray("phone-numbers")    //    "phone-numbers":[
                .writeStartObject()              //        {
                    .write("number", "555-1111") //            "number":"555-1111",
                    .write("extension", "123")   //            "extension":"123"
                .writeEnd()                      //        },
                .writeStartObject()              //        {
                    .write("number", "555-2222") //            "number":"555-2222",
                    .writeNull("extension")      //            "extension":null
                .writeEnd()                      //        }
            .writeEnd()                          //    ]
        .writeEnd()                              // }
        .close();
    }

}

Output

Below is the output from running the GeneratorDemo.

{
    "name":"Jane Doe",
    "address":{
        "type":1,
        "street":"1 A Street",
        "city":null,
        "verified":false
    },
    "phone-numbers":[
        {
            "number":"555-1111",
            "extension":"123"
        },
        {
            "number":"555-2222",
            "extension":null
        }
    ]
}

JsonParser (javax.json.stream) 

Using JsonParser we will parse the output of the previous example to get the address information. JSON parser provides a depth first traversal of events corresponding to the JSON structure.  Different data can be obtained from the JsonParser depending on the type of the event.

package blog.jsonp;

import java.io.FileInputStream;
import javax.json.Json;
import javax.json.stream.JsonParser;
import javax.json.stream.JsonParser.Event;

public class ParserDemo {

    public static void main(String[] args) throws Exception  {
        try (FileInputStream json = new FileInputStream("src/blog/jsonp/input.json")) {
            JsonParser jr = Json.createParser(json);
            Event event = null;
    
            // Advance to "address" key
            while(jr.hasNext()) {
                event = jr.next();
                if(event == Event.KEY_NAME && "address".equals(jr.getString())) {
                    event = jr.next();
                    break;
                }
            }
    
            // Output contents of "address" object
            while(event != Event.END_OBJECT) {
                switch(event) {
                    case KEY_NAME: {
                        System.out.print(jr.getString());
                        System.out.print(" = ");
                        break;
                    }
                    case VALUE_FALSE: {
                        System.out.println(false);
                        break;
                    }
                    case VALUE_NULL: {
                        System.out.println("null");
                        break;
                    }
                    case VALUE_NUMBER: {
                        if(jr.isIntegralNumber()) {
                            System.out.println(jr.getInt());
                        } else {
                            System.out.println(jr.getBigDecimal());
                        }
                       break;
                    }
                    case VALUE_STRING: {
                        System.out.println(jr.getString());
                        break;
                    }
                    case VALUE_TRUE: {
                        System.out.println(true);
                        break;
                    }
                    default: {
                    }
                }
                event = jr.next();
            }
        }
    }

}

Output

Below is the output from running the ParserDemo.

type = 1
street = 1 A Street
city = null
verified = false

MOXy and the Java API for JSON Processing (JSR-353) 

Mapping your JSON to domain objects is still the easiest way to interact with JSON.  Now that JSR-353 is finalizing we will integrating it into MOXy's JSON-binding.  You can track our progress on this using the following link:

2 comments:

  1. Anonymous CowardApril 9, 2013 at 9:52 AM

    Dint know about this before. Good intro. Thanks!
    But I do not understand why Oracle / JCP is doing this. This looks almost like Log4J and JUL fiasco of the yore. Is this really required? Probably this effort would have been better spent on something else. Jackson, Gson are pretty solid and good. Why reinvent the wheel again?

    ReplyDelete
    Replies
    1. Very good question. IMHO the purpose of the JCP isn't to reinvent the wheel but to standardize the best practices. Then implementations extend the standard and the most useful extensions get rolled back into the standard.

      If you check out the JSR-353 mailing list the following people provided input to this standard.
      - Tatu Saloranta (from Jackson)
      - Eugen Cepoi (from Genson)
      - Wen Shao (from fastJSON)

      Having a standard JSON parsing API makes it very easy to switch parsers (i.e. to a faster one) without having to modify your application.

      Also other standards will build on this one. Picture a JSON-binding standard that takes JSR-353 objects as inputs and outputs. Then having a JSON-binding standard would make it possible for JAX-RS to standardize its JSON-binding (much like leveraging JAXB for XML-binding).

      Delete

Note: Only a member of this blog may post a comment.