10 Jakarta JSON Processing
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. - Object Model API
The object model API is a high-level API that provides immutable object models for JSON object and array structures. - Streaming API
The streaming API is a low-level API designed to process large amounts of JSON data efficiently. - New Features for JSON Processing
Thejavax.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.
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.
Jakarta 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.
Parent topic: Jakarta JSON Processing
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
- Creating an Object Model from Application Code
- Navigating an Object Model
- Writing an Object Model to a Stream
Parent topic: Jakarta JSON Processing
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.
Parent topic: Object Model API
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.
Parent topic: Object Model API
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
Parent topic: Object Model API
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);
Parent topic: Object Model API
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:
Parent topic: Jakarta JSON Processing
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:
- Obtain a parser instance by invoking the
Json.createParser
static method. - Iterate over the parser events using the
JsonParser.hasNext
and theJsonParser.next
methods. - 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
Parent topic: Streaming API
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.
Parent topic: Streaming API
New Features for JSON Processing
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.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" } ] }
This section includes the following topics:
- JSON Pointer
JSON Pointer defines a string syntax for referencing a location in the target. - JSON Patch
JSON Patch defines a format for expressing a sequence of operations to be applied to a JSON document. - 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.
Parent topic: Jakarta JSON Processing
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.
Parent topic: New Features for JSON Processing
JSON Patch
JsonPatch
mainly consists of two interfaces:
JsonPatch
- Providesapply()
,toJsonArray()
methods.JsonPatchBuilder
- Providesadd()
,copy()
,move()
,replace()
,remove()
, andtest()
methods.
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.
Parent topic: New Features for JSON Processing
JSON Merge Patch
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"} |
- 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.
Parent topic: New Features for JSON Processing