JSON Processing 1.1
Most languages provide support for reading and writing text files. But when it comes to special types of documents such as XML, CSV, or JSON, processing requires handling them differently to traditional text files. Java has historically had support for XML-based, documents but the support for JSON was provided via third-party libraries. JSON itself is a lightweight data-interchange format which is a well documented standard and has become extremely successful; it has become the default format for many systems. Java had the support for processing XML documents using Java API for XML Processing (JAXP) and JSON-P, which was introduced in Java EE 7. You can now process JSON documents as well. So, JSON-P does for JSON what JAXP does for XML. The 1.1 version was an update to the earlier JSON-P specification called JSON-P 1.0. This was to keep it updated with the JSON IETF standards. While this might sound like the other JSONP (notice the lack of hyphen), which stands for JSON with Padding, this is not that. JSONP is a format used to deal with cross origin AJAX calls using GET, while JSON-P is the specification defined within Java EE, used for JSON Processing and written as JSON-P.
When dealing with any Java EE API, you would have a public API and a corresponding reference implementation. For JSON-P, here are some useful references:
JSON-P official web site | |
JSR-374 page on the JCP site | |
API and reference implementation |
The API includes support for parsing, generating, and querying JavaScript Object Notation data. This is made possible using the object model or the streaming model provided by the JSON-P API. You can consider this a low-level API, which is different to the higher level declarative JSON binding API which is also part of Java EE 8. The streaming model can be considered similar to StAX for XML for creating and reading JSON in a streaming manner, while the object model can be used to work with JSON, similar to DOM for XML.
Working with JSON documents
To get an understanding of how this works, consider the following JSON document saved in a file called demo.json
(it can be any file), which contains an array of JSON objects with the name
and priority
key-value pairs:
[ { "name": "Feature: Add support for X", "priority": 1 }, { "name": "Bug: Fix search performance", "priority": 2 }, { "name": "Feature: Create mobile page", "priority": 3 } ]
Now, before looking at the API, it is important to understand how we need to perceive this JSON document. JSON defines only two data structures, one being an array that contains a list of values and the other an object that is just a name
key-value pair. There are six value types in JSON, namely:
- String
- Number
- Object
- Array
- Boolean
- Null
In the previous document, the square brackets around the content denote an array that has multiple objects as its values. Let's take a look at the first JSON object, as follows:
{ "name": "Feature: Add support for X", "priority": 1 }
The curly braces, {}
, denote a JSON object that contains a key-value pair. The key must be a string in quotes followed by the value, which must be a valid JSON data type. In the previous case, we have the string in quotes, "Feature: Add support for X"
, and this maps to a String data type. The value of the "priority"
key is a number data type, given as 1
. Since the value can be any JSON data type, you could also have nested objects and arrays as values of the JSON object. Here's an example of that, showing the "ticket"
key having an array as its value, which contains objects:
{ "name": "Feature: Add support for X", "priority": 1, "ticket": [ { "name": "Feature: add new ticket", "priority": 2 }, { "name": "Feature: update a ticket", "priority": 2 } ] }
Having built an understanding of this document structure, let's look at the API.
JSON Processing API
JSON-P can be considered as having two core APIs:
| JSON Object Model API for a simpler way of working with JSON documents in memory. |
| JSON Streaming API, which parses a document and emits events without loading the entire document in memory. |
Let's look at what the parsing API looks like when trying to parse the previous sample document. First we need to obtain a parser using the Json
class. This class is a factory class for creating JSON processing objects:
JsonParser parser = Json.createParser(Main.class.getResourceAsStream("/sample.json"));
Next, we use the returned JsonParser
object to loop over all the entries using the hasNext()
method, similar to an iterator, and in turn invoke the next()
method, which emits a JsonParser.Event
instance. This instance will hold the current JSON entry which can be a key or value:
while (parser.hasNext()) { JsonParser.Event e = parser.next(); System.out.print(e.name()); switch (e) { case KEY_NAME: System.out.print(" - " + parser.getString()); break; case VALUE_STRING: System.out.print(" - " + parser.getString()); break; case VALUE_NUMBER: System.out.print(" - " + parser.getString()); } System.out.println(); }
Using the preceding loop, we will be able to parse and examine each entry as we go through the entire document.
Apart from creating parsers using the Json
class, we can also obtain a JsonObjectBuilder
instance which is used to create JSON object models from scratch. A one line demo is shown as follows, and creates a JsonObject
:
JsonObject json = Json.createObjectBuilder().add("name", "Feature ABC").build();
More advanced usage is possible by nesting calls to the add(...)
method, which we will look at later on. There have been many noteworthy enhancements with JSON-P 1.1, such as:
- JSON Pointer: Allows for finding specific values in a JSON document
- JSON Patch: Allows for modifying operations on a JSON document
- JSON Merge Patch: Allows for using patch operations with merge
- Addition of JSON Collectors: Allows for accumulating JSON values from streams
Additionally, JsonReader
has a new method called readValue()
which returns a JSON value from the underlying input source. Similarly, JsonWriter
was updated with another new method called write(JsonValue value)
, which allows for writing the JSON value to an output source. These additions were possible without breaking the earlier APIs because default methods were introduced in Java 8. We will go through more details about parsing and various other APIs in another chapter, but for now this should give you a starting point to begin exploring the APIs further.