Close

Java JSON processing - Object Model API

[Last Updated: Sep 8, 2018]

Object Model API (javax.json) is similar to JAXP's Document Object Model(DOM) API. Just like XML DOM processing, the complete immutable object of JSON tree is loaded into memory which can be easily navigated. It is a high-level API, implemented on top of the Steaming API, javax.json.steam (that's true at least for the reference implementation)

JSON data structures and equivalents in Object Model API


JSON Data Structure Object Model API

JSON Object: an unordered set of name/values pairs. The values have to be valid JSON Data Type (please see next section)
Example:
{"name": "Mike", "age": 23}

javax.json.JsonObject

Java Code:
JsonObjectBuilder theBuilder = Json.createObjectBuilder();
theBuilder.add("name", "Mike");
theBuilder.add("age", 23);
JsonObject jsonObject = theBuilder.build();
Json.createWriter(System.out).write(jsonObject);

JSON Array: an ordered mixed list of JSON Objects, another JSON array or comma separated values
Example:
[{name: "Mike", "age":23}, {name: "John", age=30}]

javax.json.JsonArray

Java Code:
JsonArrayBuilder theBuilder = Json.createArrayBuilder();
theBuilder.add(
        Json.createObjectBuilder()
                .add("name", "Mike")
                .add("age", 23));
theBuilder.add(
        Json.createObjectBuilder()
                .add("name", "John")
                .add("age", 30));
JsonArray jsonArray = theBuilder.build();
Json.createWriter(System.out).write(jsonArray);
                        

As we have seen in above examples, the object model API uses builder patterns to create JSON object models.

  1. javax.json.JsonObjectBuilder is used to create javax.json.JsonObject.
  2. javax.json.JsonArrayBuilder is used to create javax.json.JsonArray.
All these model/builders etc are interfaces, except for the factory class, javax.json.Json. The Json#createXYZ method calls are delegated to the underlying implementation to do the real task to return the implementation of the requested builder.

JSON Value Types and equivalents in Object Model API

What we mean by JSON Value is the data value in key/value pair. To distinguish between value types from the data structure that we explained in the last section and what are those value types, let's have a look in this diagram

As we can see the values can be string, number (integer or floating point), true/false, null, and the two structured types (nested). The nested structures can repeat any number of JSON Objects (keys/value pair) or JSON Arrays (set of JSON Objects or list of comma separated values (e.g. [1, 2, 3, 4, 5]), possibly declaring more nested structures. So JSON tree in fact can grow huge in real application scenarios.

Here are Object Model API equivalents:

You must be wondering where are boolean and null equivalents. Well, don't worry just read on.
Following table shows side by side comparisons with some examples

JSON Value Type Object Model API

JSON number: Integer, fraction, exponent
Example:
{"name": "Mike", "age": 23, "height":1.8034}

javax.json.JsonNumber

Example to read JSON number:
JsonReader reader = Json.createReader(
        /*just for example, in real application we will
        be reading it from external source*/
        new StringReader("{\"name\": \"Mike\", \"age\": 23, " +
                "\"height\":1.8034}"));

JsonObject jsonObject = reader.readObject();

//not type safe in real scenarios
int age = jsonObject.getInt("age");
System.out.println("age = " + age);

//to be type safe this time
JsonValue jsonValue = jsonObject.get("height");
if (jsonValue instanceof JsonNumber) {/*alternatively
jsonValue.getValueType()== JsonValue.ValueType.NUMBER*/
    System.out.println("height = " + ((JsonNumber) jsonValue)
            .bigDecimalValue());
}

JSON string: a sequence of zero or more double quoted Unicode characters with backslash escaping
Example:
{"message": "hi there!"}

javax.json.JsonString

Example to read string:
JsonReader reader = Json.createReader(
            new StringReader("{\"message\": \"hi there!\"}"));
    /*we are going to be type safe at this point as compare
    to the last example*/
    JsonStructure theStructure = reader.read();
    if (theStructure.getValueType() == JsonValue.ValueType.OBJECT) {
        JsonValue jsonValue = ((JsonObject) theStructure).get("message");
        if (jsonValue.getValueType() == JsonValue.ValueType.STRING) {
            System.out.println("message = " +
                    ((JsonString) jsonValue).getString());
        }
    }     

JSON boolean:  true or false value without quotes

Example:
{"name": "Mike", "married": false}

No class level type but have constants:
javax.json.JsonValue.TRUE
javax.json.JsonValue.FALSE

Example to read boolean:

JsonReader reader = Json.createReader(
        new StringReader("{\"name\": \"Mike\", \"married\": false}"));
JsonStructure theStructure = reader.read();
if (theStructure instanceof JsonObject) {
    JsonValue jsonValue = ((JsonObject) theStructure).get("married");
    /*you will notice a difference here, but you can still do the something
     like jsonValue.getValueType() == JsonValue.ValueType.TRUE,
     I think getValueType is little inconsistent approach, they should
     have created a new class JSONBoolean instead and we could just use
     instanceof operator all the time.
     */
    if (jsonValue == JsonValue.TRUE || jsonValue == JsonValue.FALSE) {
        System.out.println("married = " + jsonValue);
    }
}

JSON null:  indicates not assigned value. Also it is need for value types other than strings (which can be empty) to indicate value is empty or does not have a value

Example:
{"name": "Mike", "address": null}

No class level type but have a constant:
javax.json.JsonValue.NULL

Example to read null:


JsonReader reader = Json.createReader(
        new StringReader("{\"name\": \"Mike\", \"address\": null}"));
JsonStructure theStructure = reader.read();
if(theStructure instanceof JsonObject){
    JsonValue jsonValue = ((JsonObject) theStructure).get("address");
    if(jsonValue == JsonValue.NULL){
        System.out.println("address is "+jsonValue);
    }
}

Please download the java source of above examples if you want to play with them.

We cannot explicitly create instance of any JsonType

In all recent examples to demonstrate the use of JsonType(s), we read JSON source rather than creating Object Model with a JsonType to generate JSON. This is because with current API there is no way to create those types. We do not have any factory method or builder approach to create one. For example JsonNumber cannot be created explicitly. Is that a bad thing? I guess not. We actually do not need that. We just have to pass Java literals or appropriate variables to one of the appropriate JsonObjectBuilder#addXyz methods. The underlying implementation does the suitable implicit conversion of those literals/variable-values to JSON types. There's one method though :JsonObjectBuilder#add(String name, JsonValue value). Why do we have it? Well, we can still pass those boolean/null constants. Or may be we want to reuse some existing JsonValue, for example retrieved from read process earlier.

Now another question might be: why do we need those types at all then? Well, that is because we really need Java equivalent JSON types when our application read external JSON source. That enable us to write type safe code and of course we need to know, what type of JSON we are dealing with rather than doing low level parsing ourselves .There's one more advantage: in case of JsonNumber we have various options to what Java type we want to convert to, which gives us more control over application logic and also saves us do those number conversions ourselves . Some of the those JsonNumber methods are: JsonNumber#intValue(), JsonNumber#longValue(), JsonNumber#longValue() JsonNumber#bigDecimalValue() etc.

The API in a Nutshell

It's always a good idea to hav a quick look at the class hierarchy whenever we learn some new API. Here is complete class hierarchy for javax.json package

We can see there are couple of builder and factory classes. The builders are for creating JsonStructure types. There are reader/writer factories. In our examples we will usually be using Json#createReader and Json#writer which return instances of javax.json.JsonReader and javax.json.JsonWriter respectively. According to the java docs, reader/writer factories are preferred way if multiple reader instances have to be created.

One important thing to notice here is, other than javax.json.JsonValue, javax.json.JsonObject is also extending java.util.Map. I don't think it is a good design decision as we are exposing unnecessary methods of Map to JsonObject. For example think about JsonObject#get(Object key) (inherited from Map). The Object type key is too generic in JsonObject scenario as it only should accept key of type java.lang.String. Also JsonObject is immutable but we still have Map#put available, If we try to use we will get java.lang.UnsupportedOperationException. Not good, as we are shifting compile type strong type checking to runtime exception. This is the similar mistake which was made on java.util.Properties extending java.util.Hashtable. I would rather prefer Composition over inheritance in this kind of cases. Another similar bad decision is: javax.json.JsonArray extending java.util.List

See Also