JSON-P is an intriguing API because it was originally considered for inclusion in Java SE, but was made available to Java EE instead. Chapter 12 explores JSON-P.
What Is JSON-P?
JSON Processing (JSON-P) is a Java API for processing (i.e., parsing, generating, querying, and transforming) JSON content.
JSON-P 1.0
JSON-P 1.0 processes JSON content via an object model or a streaming model. The object model lets JSON-P build a tree of objects for JSON text via API classes similarly to Java’s DOM API for XML. The streaming model lets JSON-P produce and consume JSON text similarly to Java’s StAX API for XML.
JSON-P 1.0 began as “Java Specification Request (JSR) 353: Java API for JSON Processing” ( http://jcp.org/en/jsr/detail?id=353 ). It was officially released in May 2013, but only for Java EE 7 and higher. However, it also can be used in a Java SE 6 and higher context.
JSON-P 1.0 consists of 25 types located in package javax.json, along with the support packages javax.json.spi and javax.json.stream. The javax.json package mainly contains types that support the object model, the javax.json.spi package contains a single type that describes a service provider for JSON processing objects, and the javax.json.stream package contains types that support the streaming model. Both models are discussed later.
JsonArray : an interface that represents an immutable JSON array (an ordered sequence of zero or more values)
JsonArrayBuilder : an interface that describes a builder for creating JsonArray models from scratch
JsonBuilderFactory : an interface that describes a factory for creating JsonArrayBuilder and JsonObjectBuilder objects
JsonNumber : an interface that represents an immutable JSON number value
JsonObject : an interface that represents an immutable JSON object value (an unordered collection of zero or more name/value pairs)
JsonObjectBuilder : an interface that describes a builder for creating JsonObject models from scratch
JsonReader : an interface that describes a way to read a JSON object or an array structure from an input source
JsonReaderFactory : an interface that describes a factory for creating JsonReader objects
JsonString : an interface that represents an immutable JSON string
JsonStructure : an interface that serves as the direct supertype of JsonArray and JsonObject
JsonValue : an interface that represents an immutable JSON value, and is also the direct supertype of JsonNumber, JsonString, and JsonStructure
JsonWriter : an interface that describes a way to write a JSON object or an array structure to an output destination
JsonWriterFactory : an interface that describes a factory for creating JsonWriter objects
JsonValue.ValueType : an enum of JsonValue type constants
JsonException : an exception that identifies some kind of failure during JSON processing
Json Value Hierarchy
JSON’s Boolean and null types exist as TRUE, FALSE, and NULL constants in JsonValue.
JsonProvider : an abstract class that serves as a service provider for JSON processing objects. The JSON-P libraries include a default Glassfish ( http://en.wikipedia.org/wiki/GlassFish ) JsonProvider implementation.
JsonGenerator : an interface that describes a way to write JSON data to an output destination in a streaming manner
JsonGeneratorFactory : an interface that describes a factory for creating JsonGenerator objects
JsonLocation : an interface that describes a way to obtain the location information (e.g., column and line numbers) of a JSON event in an input source
JsonParser : an interface that describes a way to read JSON data from an input source in a streaming (and read-only) manner
JsonParserFactory : an interface that describes a factory for creating JsonParser objects
JsonParser.EVENT : an enum of event types
JsonGenerationException : an exception indicating that an incorrect JSON document is being generated
JsonParsingException : an exception indicating that an incorrect JSON document is being parsed
A complete API reference is available by pointing the browser to JSR 353’s “JSR-000353 Java API for JSON Processing 1.0 Final Release for Evaluation” ( http://download.oracle.com/otndocs/jcp/json-1_0-fr-eval-spec/index.html ) page, accepting the license agreement, downloading javax.json-api-1.0-javadoc.zip, unzipping this archive, and executing jar xvf javax.json-api-1.0-javadoc.jar on the resulting javax.json-api-1.0-javadoc.jar file. Point the web browser to the unzipped index.html file to access the API reference.
JSON-P 1.1
JSON-P 1.1 also supports JSON Pointer, JSON Patch, and JSON Merge Patch. Additionally, the new version introduces editing/transformation operations to JSON array and object builders and updates the API to better support Java SE 8 stream operations (including JSON-specific collectors).
Note
After JSON-P 1.0 was released, JSON Pointer, JSON Patch, and JSON Merge Patch specifications were released. JSON Pointer defines a string-syntax for identifying a specific value in a JSON document. JSON Patch defines a JSON document structure for expressing a sequence of operations to apply to another JSON document (and also makes use of JSON Pointer). JSON Merge Patch is similar to JSON Patch in that it’s also used to change another JSON document’s structure. However, the syntax of the JSON Merge Patch JSON document more closely mimics the syntax of the JSON document that’s being changed.
JSON-P 1.1 began as “JSR 374: Java API for JSON Processing 1.1” ( http://jcp.org/en/jsr/detail?id=374 ). It was officially released in May 2017, but only for Java EE 8 and higher. However, it also can be used in a Java SE 8 and higher context.
JsonMergePatch : an interface that represents an implementation of a JSON Merge Patch as defined by RFC 7386 ( http://tools.ietf.org/html/rfc7386 )
JsonPatch : an interface that represents an immutable implementation of a JSON Patch as defined by RFC 6902 ( http://tools.ietf.org/html/rfc6902 )
JsonPatchBuilder : an interface that describes a builder for constructing a JSON Patch by adding JSON Patch operations incrementally
JsonPointer : an interface that represents an immutable implementation of a JSON Pointer as defined by RFC 6901 ( http://tools.ietf.org/html/rfc6901 )
JsonPatch.Operation : an enum of valid RFC 6902 JSON Patch operations
JsonCollectors : a class with java.util.stream.Collector implementations for accumulating JsonValues into JsonArrays and JsonObjects
Additional changes in the form of new methods have been made to existing classes.
A complete API reference is available by pointing the browser to JSR 374’s “JSR-000374 Java API for JSON Processing 1.1 Specification Final Release for Evaluation” ( http://download.oracle.com/otndocs/jcp/json_p-1_1-final-eval-spec/index.html ) page, accepting the license agreement, downloading javax.json-api-1.1-javadoc.jar, and executing jar xvf javax.json-api-1.1-javadoc.jar. Point the web browser to the unzipped index.html file to access the API reference.
Obtaining and Using JSON-P
The latest updates of the JSON-P 1.0 and 1.1 libraries can be obtained from the Maven Repository ( http://mvnrepository.com ). At the time of writing, 1.0.4 is the latest update of JSON-P 1.0, and 1.1.3 is the latest update of JSON-P 1.1.
- 1.
Point your browser to “JSR 374 (JSON Processing) API » 1.1.3” ( http://mvnrepository.com/artifact/javax.json/javax.json-api/1.1.3 ) and click the “bundle (30 KB)” link on the Files line.
- 2.
Because the resulting javax.json-api-1.1.3.jar file doesn’t contain the Glassfish default provider, point your browser to “JSR 374 (JSON Processing) Default Provider » 1.1.3” ( http://mvnrepository.com/artifact/org.glassfish/javax.json/1.1.3 ) and click the “bundle (99 KB)” link on the Files line. You should observe a javax.json-1.1.3.jar file.
Working with JSON-P 1.0
JSON-P 1.0 provides object model and streaming model APIs to process JSON text. The object model API represents JSON text in memory via a tree-like structure, which can be navigated and queried in a random-access fashion. The streaming model API represents JSON text as a sequence of events; a parser delivers the next event to an application upon request (the event is “pulled” from the parser). The application can process or discard the event.
The object model is more flexible than the streaming model in that it enables processing that requires random access to the complete content of the tree. However, it’s often not as efficient as the streaming model and requires more memory.
Working with the Object Model API
The object model API is similar to Java’s DOM API for XML. It’s a high-level API that provides immutable object models for JSON object and array structures. These JSON structures are represented as object models using JsonObject and JsonArray, respectively.
JsonObject provides a java.util.Map view for accessing an object model’s unordered collection of zero or more name/value pairs. Similarly, JsonArray provides a java.util.List view for accessing an object model’s ordered sequence of zero or more values.
JsonObject and JsonArray are joined by JsonString and JsonNumber to represent most of JSON’s data types. Collectively, they subclass JsonValue, which also defines TRUE, FALSE, and NULL constants for JSON’s Boolean and null data types.
Builder patterns are used to create object models from scratch. Applications use interface JsonObjectBuilder to create models that represent JSON objects. The resulting model is of type JsonObject. Similarly, they use interface JsonArrayBuilder to create models that represent JSON arrays. The resulting model is of type JsonArray .
Using a Builder to Create an Object Model
Listing 12-1 describes a Java application whose main() method constructs a JsonObject describing a person. It first invokes Json’s JsonObjectBuilder createObjectBuilder() method followed by various JsonObjectBuilder add() methods to populate the JsonObject, followed by JsonObjectBuilder’s build() method to return the JsonObject.
Constructing the JsonObject requires the use of Json’s JsonArrayBuilder createArrayBuilder() method , followed by JsonArrayBuilder’s JsonArrayBuilder add(JsonObjectBuilder builder) method to construct the phone numbers JsonArray portion of the JsonObject.
After returning the JsonObject, main() invokes various methods to interrogate the object and output object details.
Object models also can be created from an input source (such as java.io.InputStream or java.io.Reader) by using interface JsonReader. Similarly, object models can be written to an output destination (such as java.io.OutputStream or java.io.Writer) by using interface JsonWriter.
Using a Reader to Create an Object Model and a Writer to Write This Model
Listing 12-2 describes a Java application whose main() method first constructs a JsonReader object by invoking Json’s JsonReader createReader(Reader reader) method with either a java.io.FileReader object to read from a person.json file or a java.io.StringReader object to read from a string when this file doesn’t exist. It then creates a JsonObject by invoking JsonReader’s JsonObject readObject() method .
main() subsequently invokes various methods to interrogate the object and output object details and then uses JsonWriter to write the object to a file named person.json.
Compile and run the application as previously shown, and you should observe the same output (and a person.json file should be created) .
Working with the Streaming Model API
The streaming model API is similar to Java’s StAX API for XML. It’s a high-level API that provides a stream of events during JSON text parsing, which is represented by interface JsonParser. This interface provides boolean hasNext() and Event next() methods for parsing JSON text from an input source.
Using the Streaming Model to Parse JSON Text
Listing 12-3 describes a Java application that constructs a parser by invoking Json’s JsonParser createParser(Reader reader) method with a StringReader object describing string-based JSON text. It repeatedly invokes JsonParser’s hasNext() and next() methods to respectively test for another event (returning true for an event or false when there are no more events) and return the event.
next() returns the next event as a JsonParser.Event enum constant. The only event of interest is the name of an object’s key, which is signified by Event.KEY_NAME. This name is returned by invoking JsonParser’s String getString() method. Once a match is detected, parser.next(); is executed to skip past this name and get to the next event, typically to return a key’s associated value.
Compile and run the application as previously shown, and you should observe the same output.
The streaming model API also supports JSON content generation, which is represented by interface JsonGenerator. This interface provides write-prefixed methods for generating JSON text to an output destination.
Using the Streaming Model to Generate JSON Text
Listing 12-4 describes a Java application whose main() method constructs a generator by invoking Json’s JsonGenerator createGenerator(Writer write) method with a FileWriter object describing a file-based destination (person.json). It invokes JsonGenerator’s JsonGenerator writeStartObject() method to begin the process of writing the object.
Various write(), writeStartObject(), writeStartArray(), and writeEnd() method calls are chained to the initial writeStartObject() call. Following this sequence, JsonGenerator’s close() method is called to close the generator.
Compile and run the application as previously shown, and you should observe no output (although a person.json file is generated) .
Working with JSON-P 1.1’s Advanced Features
JSON-P 1.1 builds on JSON-P 1.0 mainly by supporting JSON Pointer, JSON Patch, and JSON Merge Patch; by including editing/transformation operations; and by updating the API to support Java SE 8.
Note
The previous demos work unchanged under JSON-P 1.1. Simply replace javax.json-1.0.4.jar with javax.json-api-1.1.3.jar;javax.json-1.1.3.jar in the CLASSPATH.
JSON Pointer
JSON Pointer ( http://tools.ietf.org/html/rfc6901 ) defines a Unicode-string-syntax for identifying a specific value in a JSON document. Essentially, the string contains a sequence of zero or more /-prefixed reference tokens (JSON document keys or values).
The characters ~ (0x7E) and / (0x2F) have special meanings in JSON Pointer. Therefore, ~ needs to be encoded as ~0 and / needs to be encoded as ~1 when these characters appear in a reference token.
"" (the whole document)
"/name" ("Duke")
"/hobbies" (["reading","surfing","eating pizza"])
"/hobbies/0" ("reading")
Array entries are referenced via zero-based digit sequences, where /0 references the first entry, /1 references the second entry, and so on .
JSON-P represents a JSON Pointer via the JsonPointer interface. An instance of a class that implements this interface is obtained by executing Json’s JsonPointer createPointer(String jsonPointer) static method.
<T extends JsonStructure> T add(T target, JsonValue value)
<T extends JsonStructure> T remove(T target)
<T extends JsonStructure> T replace(T target, JsonValue value)
Demonstrating JSON Pointer
Listing 12-5 includes expression Json.createValue(30) for conveniently converting an int to a JsonNumber, which is the appropriate JsonValue subtype to be passed as the second argument to JsonPointer’s replace() method. The JsonNumber createValue(int value) method is one of six overloaded static methods added in JSON-P 1.1 for conveniently converting doubles, ints, longs, java.lang.Strings, java.math.BigDecimals, and java.math.BigIntegers to their JSON-P equivalents.
String encodePointer(String pointer)
String decodePointer(String encPointer)
Demonstrating JSON Pointer Encoding and Decoding
JSON Patch
JSON Patch ( http://tools.ietf.org/html/rfc6902 ) defines a JSON document structure for expressing a sequence of operations to apply to a target JSON document. It’s often used with HTTP PATCH ( http://en.wikipedia.org/wiki/Patch_verb ), an HTTP request method for making partial changes to an existing resource.
Each object has an op field that identifies the operation to be performed. Each object also has a path field, which is a JSON Pointer that references a location in the target document.
test: Test that the value at the path location is equal to the value assigned to the value field.
remove: Remove the value at the path location.
add: Add a new element to an array or a new field to an object, or replace an existing object field’s value.
replace: Replace the value at the path location with a new value.
move: Remove the value at the from location and add it to the path location.
copy: Copy the value at the from location to the path location.
Demonstrating JSON Patch
Creating a JSON Patch Based on Differences Between a Source and a Target
JSON Merge Patch
JSON Merge Patch ( http://tools.ietf.org/html/rfc7386 ) defines a JSON document structure for expressing a sequence of changes to be made to a target JSON document via a syntax that closely mimics that document. Recipients of a JSON Merge Patch document determine the exact set of changes being requested by comparing the content of the provided patch against the current content of the target document. If the patch contains members that don’t appear in the target, those members are added. If the target does contain the member, the value is replaced. Null values in the merge patch are given special meaning to indicate the removal of existing values in the target. As with JSON Patch, JSON Merge Patch is often used with HTTP PATCH ( http://en.wikipedia.org/wiki/Patch_verb ).
Demonstrating JSON Merge Patch
Listing 12-9 reveals that an array cannot be manipulated by merge patches. To add an element to an array, or to mutate any of its elements, the entire resulting array must be specified.
It’s necessary to cast mergePatch.apply(person) to JsonObject in order to avoid an incompatible-type error: JsonValue cannot be converted to JsonObject.
Creating a JSON Merge Patch Based on Differences Between a Source and a Target
It’s necessary to invoke toJsonValue() on the JsonMergePatch returned from createMergeDiff() in order to see a JSON document as the output.
Editing/Transformation Operations
The JSON object model supports immutable JSON objects and arrays, which makes these entities thread-safe. Because you cannot modify them, you would manually copy each of the properties of the JSON object/array into a JsonObjectBuilder or JsonArrayBuilder and then modify the entity, which is a rather tedious task. Thankfully, JSON-P 1.1 alleviates the tedium by adding editing/transformation operations to JsonArrayBuilder and JsonObjectBuilder.
Demonstrating Assorted Editing/Transformation Operations
Listing 12-11 first demonstrates editing/transformation in an array builder context. A list of planet names is constructed, and this list is passed to Json’s JsonArrayBuilder createArrayBuilder(Collection<?> collection) static method. Various editing/transformation method calls are chained to createArrayBuilder() to remove Pluto (which is no longer officially a planet), change Terra to Earth, and add Neptune. The build() call returns a JsonArray, which is printed to standard output.
Listing 12-11 next demonstrates editing/transformation in an object builder context. A map of employee names and ages is constructed, and this map is passed to Json’s JsonObjectBuilder createObjectBuilder(Map<String, Object> map) static method. A remove() method call is chained to createObjectBuilder() to remove Jill Smith. The build() call returns a JsonObject, which is printed to standard output.
Java SE 8 Support
This toJsonObject() collector method constructs a java.util.stream.Collector that accumulates the input JsonValue elements into a JsonObject. The name/value pairs of the JsonObject are computed by applying the provided keyMapper and valueMapper mapping functions.
Demonstrating a JSON-P Collector
Listing 12-12’s main() method first creates a JSON array of employee objects where each object consists of a name and an age. After outputting the array, it returns a list view of the array, obtains a stream with the list view as the source, installs (on the stream) a filter that accepts only those entries whose age exceeds 40, and installs (on the stream) a collector that accumulates results into a JSON object. The results are accumulated and output .
asJsonObject() is a JsonValue method that returns the invoking JsonValue as a JsonObject. Although it’s possible to invoke getString("name") on the JsonObject, it’s not possible to invoke getInt("age") because getInt() returns a primitive int value instead of an Integer object. For this reason, the code invokes JsonObject’s inherited get() method from the Map ancestor type.
Note
JsonValue also conveniently provides an asJsonArray() method that returns the invoking JsonValue as a JsonArray.
Listing 12-12 reveals JsonArray’s <T extends JsonValue> List<T> getValuesAs(Class<T> clazz) method, which returns a list view of the specified type for the array. This method has been present since JSON-P 1.0. However, JSON-P 1.1 introduced the more functional <T,K extends JsonValue> List<T> getValuesAs(Function<K,T> func) default method, which can accept a lambda or method reference argument—Java SE 8 introduced default methods.
JSON-P 1.1’s JsonParser class has been upgraded to support Java SE 8 by including the Stream<JsonValue> getArrayStream() and Stream<Map.Entry<String,JsonValue>> getObjectStream() default methods. When called, getArrayStream() returns a stream of JsonArray elements, and getObjectStream() returns a stream of JsonObject name/value pairs.
A Sample Library JSON Document
Efficiently Processing a Potentially Large JSON Document
JsonParser offers void skipArray() and void skipObject() methods that skip tokens and advance the parser to END_ARRAY or END_OBJECT, respectively.
Exercises
- 1.
Define JSON-P.
- 2.
Describe JSON-P 1.0’s package structure.
- 3.
Identify the types for creating JsonArray and JsonObject models.
- 4.
Which type is the superinterface of interface types that represent immutable JSON values?
- 5.
In what ways does JSON-P 1.1 differ from JSON-P 1.0?
- 6.
True or false: JSON-P 1.0’s streaming model is more flexible than its object model.
- 7.
How do you construct a JsonObject model?
- 8.
What types does the streaming model provide to read and write JSON content?
- 9.
Describe JSON Merge Patch.
- 10.
Identify the editing/transformation operations that have been added to JsonArrayBuilder and JsonObjectBuilder.
- 11.
True or false: JSON-P 1.1 introduced a JsonCollectors class whose static collector methods return JSONObjects or JsonArrays instead of Maps or Lists.
- 12.
Write a JSONPDemo application that takes two command-line arguments: the name of a JSON document file and a JSON Pointer. The application should read the file into a JsonObject model (assume that every JSON document file defines an object). The application should then create a JsonPointer for the JSON Pointer argument and apply the JsonPointer instance to the JsonObject, outputting the result. Hint: Refer to the “JSON Pointer” section for an example JSON document and sample JSON Pointers that access different document members.
Summary
JSON Processing (JSON-P) is a Java API for processing (i.e., parsing, generating, querying, and transforming) JSON content.
JSON-P 1.0 processes JSON content via an object model or a streaming model. The object model lets JSON-P build a tree of objects for JSON text via API classes similarly to Java’s DOM API for XML. The streaming model lets JSON-P produce and consume JSON text similarly to Java’s StAX API for XML.
JSON-P 1.0 consists of 25 types located in package javax.json, along with the support packages javax.json.spi and javax.json.stream. The javax.json package mainly contains types that support the object model, the javax.json.spi package contains a single type that describes a service provider for JSON processing objects, and the javax.json.stream package contains types that support the streaming model.
JSON-P 1.1 also supports JSON Pointer, JSON Patch, and JSON Merge Patch. Additionally, the new version introduces editing/transformation operations to JSON array and object builders and updates the API to better support Java SE 8 stream operations (including JSON-specific collectors).
JSON-P 1.1 consists of the same packages as JSON-P 1.0 but increases the number of types to 31.
JSON Pointer defines a Unicode-string-syntax for identifying a specific value in a JSON document. JSON Patch defines a JSON document structure for expressing a sequence of operations to apply to another JSON document (and also makes use of JSON Pointer). JSON Merge Patch is similar to JSON Patch in that it’s also used to change another JSON document’s structure. However, the syntax of the JSON Merge Patch JSON document more closely mimics the syntax of the JSON document that’s being changed.
Several editing/transformation operations have been introduced to JsonArrayBuilder and JsonObjectBuilder. JsonArrayBuilder has received several add(int index, ...) methods for adding a value between array elements, several set(int index, ...) methods for updating an existing element, and a remove() method for removing an element. JsonObjectBuilder has received a remove() method for removing a name/value pair.
Queries on a JSON object model are currently possible via Java SE 8’s stream operations and lambda expressions. To make them truly useful and convenient, JSON-P 1.1 introduced a JsonCollectors class whose static collector methods return JSONObjects or JsonArrays instead of Maps or Lists.
Appendix A presents the answers to each chapter’s exercises.