10 Java API for JSON Processing

WebLogic Server supports the Java API for JSON Processing 1.1 (JSR 374) by including the JSR-374 reference implementation for use with applications deployed on a WebLogic Server instance.

This chapter includes the following sections:

To learn more about JSON concepts, see Java API for JSON Processing in the Java EE 8 Tutorial.

About JavaScript Object Notation (JSON)

JSON is a lightweight data-interchange format that is widely used as a common format to serialize and deserialize data in applications that communicate with each other over the Internet. These applications are often created using different programming languages and run in very different environments.

JSON is suited to this scenario because it is an open standard, it is easy to read and write, and it is more compact than other representations. RESTful web services typically make extensive use of JSON as the format for the data inside requests and responses, with the JSON representations usually being more compact than the counterpart XML representations since JSON does not have closing tags.

The Java API for JSON Processing provides a convenient way to process (parse, generate, transform, and query) JSON text. For generating and parsing JSON data, there are two programming models, which are similar to those used for XML documents:

  • The object model creates a tree that represents the JSON data in memory. The tree can then be navigated and analyzed. Although the JSON data created in memory is immutable and cannot be modified, the object model is the most flexible and allows for processing that requires access to the complete contents of the tree. However, it is often slower than the streaming model and requires more memory. The object model generates JSON output by navigating the entire tree at once.

    For information about using the object model, see Object Model API.

  • The streaming model uses an event-based parser that reads JSON data one element at a time. The parser generates events and stops for processing when an object or an array begins or ends, when it finds a key, or when it finds a value. Each element can be processed or discarded by the application code, and then the parser proceeds to the next event. This approach is adequate for local processing, in which the processing of an element does not require information from the rest of the data. The streaming model generates JSON output to a given stream by making a function call with one element at a time.

    For information about using the streaming model, see Streaming API.

Object Model API

The object model API is a high-level API that provides immutable object models for JSON object and array structures.

These JSON structures are represented as object models using the Java types JsonObject and JsonArray. The interface javax.json.JsonObject provides a map view to access the unordered collection of zero or more name-value pairs from the model. Similarly, the javax.json.JsonArray interface provides a list view to access the ordered sequence of zero or more values from the model.

The object model API uses builder patterns to create these object models. The javax.json.JsonObjectBuilder and javax.json.JsonArrayBuilder interfaces provide methods to create models of type JsonObject and JsonArray, respectively.

These object models can also be created from an input source using the javax.json.JsonReader interface. Similarly, these object models can be written to an output source using the javax.json.JsonWriter interface.

The following sections show examples of using the object model API:

Creating an Object Model from JSON Data

The following example shows how to create an object model from JSON data in a text file:

import java.io.FileReader;
import javax.json.Json;
import javax.json.JsonReader;
import javax.json.JsonStructure;
...
JsonReader reader = Json.createReader(new FileReader("jsondata.txt"));
JsonStructure jsonst = reader.read();

The object reference jsonst can be either of type JsonObject or of type JsonArray, depending on the contents of the file. JsonObject and JsonArray are subtypes of JsonStructure. This reference represents the top of the tree and can be used to navigate the tree or to write it to a stream as JSON data.

Creating an Object Model from Application Code

The following example shows how to create an object model from application code:

import javax.json.Json;
import javax.json.JsonObject;
...
JsonObject model = Json.createObjectBuilder()
   .add("firstName", "Duke")
   .add("lastName", "Java")
   .add("age", 18)
   .add("streetAddress", "100 Internet Dr")
   .add("city", "JavaTown")
   .add("state", "JA")
   .add("postalCode", "12345")
   .add("phoneNumbers", Json.createArrayBuilder()
      .add(Json.createObjectBuilder()
         .add("type", "mobile")
         .add("number", "111-111-1111"))
      .add(Json.createObjectBuilder()
         .add("type", "home")
         .add("number", "222-222-2222")))
   .build();

The object reference model represents the top of the tree, which is created by nesting invocations to the add methods and is built by invoking the build method. The javax.json.JsonObjectBuilder interface contains the following add methods:

JsonObjectBuilder add(String name, BigDecimal value)
JsonObjectBuilder add(String name, BigInteger value)
JsonObjectBuilder add(String name, boolean value)
JsonObjectBuilder add(String name, double value)
JsonObjectBuilder add(String name, int value)
JsonObjectBuilder add(String name, JsonArrayBuilder builder)
JsonObjectBuilder add(String name, JsonObjectBuilder builder)
JsonObjectBuilder add(String name, JsonValue value)
JsonObjectBuilder add(String name, long value)
JsonObjectBuilder add(String name, String value)
JsonObjectBuilder addNull(String name)

The javax.json.JsonArrayBuilder interface contains similar add methods that do not have a name (key) parameter. You can nest arrays and objects by passing a new JsonArrayBuilder object or a new JsonObjectBuilder object to the corresponding add method, as shown in this example.

The resulting tree represents the JSON data from JSON Syntax.

Navigating an Object Model

The following example shows a simple approach to navigating an object model:

import javax.json.JsonValue;
import javax.json.JsonObject;
import javax.json.JsonArray;
import javax.json.JsonNumber;
import javax.json.JsonString;
...
public static void navigateTree(JsonValue tree, String key) {
   if (key != null)
      System.out.print("Key " + key + ": ");
   switch(tree.getValueType()) {
      case OBJECT:
         System.out.println("OBJECT");
         JsonObject object = (JsonObject) tree;
         for (String name : object.keySet())
            navigateTree(object.get(name), name);
         break;
      case ARRAY:
         System.out.println("ARRAY");
         JsonArray array = (JsonArray) tree;
         for (JsonValue val : array)
            navigateTree(val, null);
         break;
      case STRING:
         JsonString st = (JsonString) tree;
         System.out.println("STRING " + st.getString());
         break;
      case NUMBER:
         JsonNumber num = (JsonNumber) tree;
         System.out.println("NUMBER " + num.toString());
         break;
      case TRUE:
      case FALSE:
      case NULL:
         System.out.println(tree.getValueType().toString());
         break;
   }
}

The navigateTree method can be used with the models shown in Creating an Object Model from JSON Data and Creating an Object Model from Application Code as follows:

navigateTree(model, null);

The navigateTree method takes two arguments: a JSON element and a key. The key is used only to help print the key-value pairs inside objects. Elements in a tree are represented by the JsonValue type. If the element is an object or an array, a new invocation to this method is made for every element contained in the object or array. If the element is a value, it is printed to standard output.

The JsonValue.getValueType method identifies the element as an object, an array, or a value. For objects, the JsonObject.keySet method returns a set of strings that contains the keys in the object, and the JsonObject.get(String name) method returns the value of the element whose key is name. For arrays, JsonArray implements the List<JsonValue> interface. You can use enhanced for loops with the Set<String> instance returned by JsonObject.keySet and with instances of JsonArray, as shown in this example.

The navigateTree method for the model shown in Creating an Object Model from Application Code produces the following output:

OBJECT
Key firstName: STRING Duke
Key lastName: STRING Java
Key age: NUMBER 18
Key streetAddress: STRING 100 Internet Dr
Key city: STRING JavaTown
Key state: STRING JA
Key postalCode: STRING 12345
Key phoneNumbers: ARRAY
OBJECT
Key type: STRING mobile
Key number: STRING 111-111-1111
OBJECT
Key type: STRING home
Key number: STRING 222-222-2222

Writing an Object Model to a Stream

The object models created in Creating an Object Model from JSON Data and Creating an Object Model from Application Code can be written to a stream using the javax.json.JsonWriter interface as follows:

import java.io.StringWriter;
import javax.json.JsonWriter;
...
StringWriter stWriter = new StringWriter();
JsonWriter jsonWriter = Json.createWriter(stWriter);
jsonWriter.writeObject(model);
jsonWriter.close();
 
String jsonData = stWriter.toString();
System.out.println(jsonData);
 
The Json.createWriter method takes an output stream as a parameter. The JsonWriter.writeObject method writes the object to the stream. The JsonWriter.close method closes the underlying output stream.
 
The following example uses try-with-resources to close the JSON writer automatically:
 
StringWriter stWriter = new StringWriter();
try (JsonWriter jsonWriter = Json.createWriter(stWriter)) {
   jsonWriter.writeObject(model);
}
 
String jsonData = stWriter.toString();
System.out.println(jsonData);

Streaming API

The streaming API is a low-level API designed to process large amounts of JSON data efficiently.

This API consists of the following interfaces:

Interface Description
javax.json.stream.JsonParser

Contains methods to parse JSON in a streaming way.

This interface provides forward, read-only access to JSON data using the pull parsing programming model. In this model the application code controls the thread and calls methods in the parser interface to move the parser forward or to obtain JSON data from the current state of the parser.

javax.json.stream.JsonGenerator

Contains methods to write JSON to an output source in a streaming way.

This interface provides methods to write JSON to an output source. The generator writes name-value pairs in JSON objects and values in JSON arrays.

The following sections show examples of using the streaming API:

Reading JSON Data Using a Parser

The streaming API is the most efficient approach for parsing JSON text. The following example shows how to create a JsonParser object and how to parse JSON data using events:

import javax.json.Json;
import javax.json.stream.JsonParser;
...
JsonParser parser = Json.createParser(new StringReader(jsonData));
while (parser.hasNext()) {
   JsonParser.Event event = parser.next();
   switch(event) {
      case START_ARRAY:
      case END_ARRAY:
      case START_OBJECT:
      case END_OBJECT:
      case VALUE_FALSE:
      case VALUE_NULL:
      case VALUE_TRUE:
         System.out.println(event.toString());
         break;
      case KEY_NAME:
         System.out.print(event.toString() + " " +
                          parser.getString() + " - ");
         break;
      case VALUE_STRING:
      case VALUE_NUMBER:
         System.out.println(event.toString() + " " +
                            parser.getString());
         break;
   }
}

This example consists of three steps:

  1. Obtain a parser instance by invoking the Json.createParser static method.
  2. Iterate over the parser events using the JsonParser.hasNext and the JsonParser.next methods.
  3. Perform local processing for each element.

The example shows the ten possible event types from the parser. The parser's next method advances it to the next event.

For the event types KEY_NAME, VALUE_STRING, and VALUE_NUMBER, you can obtain the content of the element by invoking the JsonParser.getString method.

For VALUE_NUMBER events, you can also use the following methods:

START_OBJECT
KEY_NAME firstName - VALUE_STRING Duke
KEY_NAME lastName - VALUE_STRING Java
KEY_NAME age - VALUE_NUMBER 18
KEY_NAME streetAddress - VALUE_STRING 100 Internet Dr
KEY_NAME city - VALUE_STRING JavaTown
KEY_NAME state - VALUE_STRING JA
KEY_NAME postalCode - VALUE_STRING 12345
KEY_NAME phoneNumbers - START_ARRAY
START_OBJECT
KEY_NAME type - VALUE_STRING mobile
KEY_NAME number - VALUE_STRING 111-111-1111
END_OBJECT
START_OBJECT
KEY_NAME type - VALUE_STRING home
KEY_NAME number - VALUE_STRING 222-222-2222
END_OBJECT
END_ARRAY
END_OBJECT

Writing JSON Data Using a Generator

The following example shows how to write JSON data to a file using the streaming API:

FileWriter writer = new FileWriter("test.txt");
JsonGenerator gen = Json.createGenerator(writer);
gen.writeStartObject()
   .write("firstName", "Duke")
   .write("lastName", "Java")
   .write("age", 18)
   .write("streetAddress", "100 Internet Dr")
   .write("city", "JavaTown")
   .write("state", "JA")
   .write("postalCode", "12345")
   .writeStartArray("phoneNumbers")
      .writeStartObject()
         .write("type", "mobile")
         .write("number", "111-111-1111")
      .writeEnd()
      .writeStartObject()
         .write("type", "home")
         .write("number", "222-222-2222")
      .writeEnd()
   .writeEnd()
.writeEnd();
gen.close();

This example obtains a JSON generator by invoking the Json.createGenerator static method, which takes a writer or an output stream as a parameter. The example writes JSON data to the test.txt file by nesting invocations to the write, writeStartArray, writeStartObject, and writeEnd methods. The JsonGenerator.close method closes the underlying writer or output stream.

New Features for JSON Processing

The javax.json API supports new features of JSON Processing such as JSON Pointer, JSON Patch, and JSON Merge Patch. These features can be used to retrieve, transform or manipulate values in an object model.

This section includes the following topics:

In this section, the following sample JSON document is used to demonstrate the new features of JSON Processing. This sample contains name-value pairs and the value for the name "phoneNumbers" used in this sample, is an array whose elements are two objects.

{
   "firstName": "Duke",
   "lastName": "Java",
   "age": 18,
   "streetAddress": "100 Internet Dr",
   "city": "JavaTown",
   "state": "JA",
   "postalCode": "12345",
   "phoneNumbers": [
      { "Mobile": "111-111-1111" },
      { "Home": "222-222-2222" }
   ]
}

JSON Pointer

JSON Pointer defines a string syntax for referencing a location in the target.

A JSON Pointer, when applied to a target JsonValue, defines a reference location in the target. An empty JSON Pointer string defines a reference to the target itself.

JsonPointer provides the following methods:

where contacts is JsonObject contacts = Json.createReader(new StringReader(jsonstring)).readObject();

  • add() - Adds new value or member.
    /*add*/
    JsonPointer pointer = Json.createPointer("/email");
    contacts = pointer.add(contacts,Json.createValue("duke@example.com"));
    
  • containsValue() - Checks if the value is present.
    /*containsValue*/
    JsonPointer pName = Json.createPointer("/firstName");
    boolean exist = pName.containsValue(“John”);
  • getValue() - Fetches a single value.
    /*getValue*/
    JsonPointer pPhone = Json.createPointer("/phoneNumber/0");
    Jsonvalue mobileNumber = (pPhone.getValue(contacts);
  • remove() - Removes the value at the target location.
    /*remove*/
    JsonPointer pRemove = Json.createPointer("/phoneNumber/0");
    contacts = pRemove.remove(contacts);
  • replace() - Replaces the value at the target location.
    /*replace*/
    JsonPointer pAge = Json.createPointer("/age");
    pAge.replace(contacts,30);

The following is the resultant JSON document after running the JSON Pointer examples:

{
   "firstName": "Duke",
   "lastName": "Java",
   "age": 30,
   "email": "duke@example.com",
   "streetAddress": "100 Internet Dr",
   "city": "JavaTown",
   "state": "JA",
   "postalCode": "12345",
   "phoneNumbers": [
      { "Home": "222-222-2222" }
   ]
}

See JSON Pointer RFC.

JSON Patch

JSON Patch defines a format for expressing a sequence of operations to be applied to a JSON document.
JsonPatch mainly consists of two interfaces:
  • JsonPatch - Provides apply(), toJsonArray() methods.
  • JsonPatchBuilder - Provides add(), copy(), move(), replace(), remove(), and test() methods.
A JsonPatch can be constructed using the following approaches:
  • Constructing a JSON Patch with JsonPatchBuilder
    JsonObject contacts = buildPerson();
    JsonPatchBuilder builder = Json.createPatchBuilder();
    JsonObject result = builder.add("/email","john@example.com")          
                                .replace("/age", 30)
                                .remove("/phoneNumber")       
                                .test("/firstName", "John")            
                                .copy("/address/lastName", "/lastName")         
                                .build()
                                .apply(contacts);
  • Constructing a JSON Patch with JsonPatch
    JsonArray patch=Json.createArrayBuilder().add(Json.createObjectBuilder()
                                                .add("op","replace")
                                                .add("path","/age")
                                                .add("value", 30))
                                                .build();
    JsonPatch jsonPatch = Json.createPatch(patch);
    JsonObject result1 = jsonPatch.apply(buildPerson());
    command: [{"op":"replace","path":"/age","value":30}

See JSON Patch RFC.

JSON Merge Patch

JSON Merge Patch defines a format and processing rules for applying operations to a JSON document that are based upon specific content of the target document.

JsonMergePatch describes changes to be made to a target JSON document using a syntax that closely mimics the document being modified.

Table 10-1 JsonMergePatch syntax

Original Patch Result
{"a":"b"} {"a":"c"} {"a":"c"}
{"a":"b"} {“b":"c"} {"a":"b","b":"c"}
{"a":"b"} {"a":null} {}
{"a":"b“,"b":"c"} {"a":null} {"b":"c"}
You can create a JSON Merge Patch from:
  • An existing JSONMergePatch
    JsonValue contacts = ... ; // The target to be patched
    JsonValue patch = ...  ; // JSON Merge Patch
    JsonMergePatch mergePatch = Json.createMergePatch(patch);
    JsonValue result = mergePatch.apply(contacts);
  • A difference between two JsonValues
    //The source object
    JsonValue source = ... ;
    // The modified object
    JsonValue target = ... ;
    // The diff between source and target in a Json Merge Patch format
    JsonMergePatch mergePatch = Json.createMergeDiff(source,target);

See JSON Merge Patch RFC.

If you selected to install the Server Examples, the JSON P 1.1 examples are located in the ORACLE_HOME\wlserver\samples\server\examples\src\examples\javaee8\jsonp directory, where ORACLE_HOME represents the directory in which you installed WebLogic Server.