© Jeff Friesen 2019
Jeff FriesenJava XML and JSONhttps://doi.org/10.1007/978-1-4842-4330-5_12

12. Processing JSON with JSON-P

Jeff Friesen1 
(1)
Dauphin, MB, Canada
 

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.

The javax.json package provides the entry-point Json factory class for creating JSON processing objects, such as array builders, object builders, readers, writers, parsers, and generators, and additional types that collectively define an object model API for processing JSON text:
  • 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

JsonValue is the superinterface of interface types that represent immutable JSON values. Figure 12-1 shows their hierarchical relationship.
../images/394211_2_En_12_Chapter/394211_2_En_12_Fig1_HTML.png
Figure 12-1

Relating JSON value types

JSON’s Boolean and null types exist as TRUE, FALSE, and NULL constants in JsonValue.

The javax.json.spi service provider interface package provides a single type for plugging in JSON processing object implementations:
  • 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.

Finally, the javax.json.stream package provides several types that collectively define a streaming API for parsing and generating JSON text:
  • 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.

JSON-P 1.1 consists of 31 types that are located in the main package javax.json, along with the support packages javax.json.spi and javax.json.stream. (No new packages have been added.) The following new types have been added to javax.json :
  • 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

The following new type is located in javax.json.stream:
  • 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.

Obtain version 1.0.4 by pointing your browser to “JSR 374 (JSON Processing) Default Provider » 1.0.4” ( http://mvnrepository.com/artifact/org.glassfish/javax.json/1.0.4 ) and clicking the “bundle (83 KB)” link on the Files line. The resulting javax.json-1.0.4.jar file contains all of the JSON-P 1.0 classfiles along with the Glassfish default provider classfiles. Add this JAR file to your CLASSPATH when compiling and running code that uses this library:
javac -cp javax.json-1.0.4.jar source file
java -cp javax.json-1.0.4.jar;. main classfile
Follow these steps to obtain version 1.1.3:
  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. 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.

     
javax.json-api-1.1.3.jar contains only the JSON-P 1.1 class files, and javax.json-1.1.3.jar contains only the Glassfish class files. Add these JAR files to your CLASSPATH when compiling and running code that uses these libraries:
javac -cp javax.json-api-1.1.3.jar;javax.json-1.1.3.jar;. source file
java -cp javax.json-api-1.1.3.jar;javax.json-1.1.3.jar;. main classfile

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 .

Listing 12-1 presents the source code to an application that demonstrates the builder approach to creating an object model.
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import static java.lang.System.*;
public class JSONPDemo
{
   public static void main(String[] args)
   {
      JsonObject person =
         Json.createObjectBuilder()
             .add("firstName", "John")
             .add("lastName", "Doe")
             .add("age", 42)
             .add("address", Json.createObjectBuilder()
                                 .add("street",
                                      "400 Some Street")
                                 .add("city",
                                      "Beverly Hills")
                                 .add("state", "CA")
             .add("zipcode", 90210))
             .add("phoneNumbers",
                  Json.createArrayBuilder()
                      .add(Json.createObjectBuilder()
                               .add("type", "home")
                               .add("number",
                                    "310 555-1234"))
                             .add(Json.createObjectBuilder()
                                      .add("type", "fax")
                                      .add("number",
                                           "310 555-4567")))
             .build();
      out.printf("First name: %s%n",
                 person.getString("firstName"));
      out.printf("Last name: %s%n",
                 person.getString("lastName"));
      out.printf("Age: %d%n", person.getInt("age"));
      out.println("Address");
      out.println("-------");
      JsonObject address = person.getJsonObject("address");
      out.printf("   Street: %s%n",
                 address.getString("street"));
      out.printf("   City: %s%n",
                 address.getString("city"));
      out.printf("   State: %s%n",
                 address.getString("state"));
      out.printf("   Zipcode: %d%n",
                 address.getInt("zipcode"));
      out.println("Phone Numbers");
      out.println("-------------");
      JsonArray phoneNumbers =
         person.getJsonArray("phoneNumbers");
      for (JsonObject phoneNumber:
           phoneNumbers.getValuesAs(JsonObject.class))
      {
         out.printf("   Type: %s%n",
                    phoneNumber.getString("type"));
         out.printf("   Number: %s%n",
                    phoneNumber.getString("number"));
      }
   }
}
Listing 12-1

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.

Compile Listing 12-1 as follows:
javac -cp javax.json-1.0.4.jar JSONPDemo.java
Run the resulting application as follows:
java -cp javax.json-1.0.4.jar;. JSONPDemo
You should observe the following output:
First name: John
Last name: Doe
Age: 42
Address
-------
   Street: 400 Some Street
   City: Beverly Hills
   State: CA
   Zipcode: 90210
Phone Numbers
-------------
   Type: home
   Number: 310 555-1234
   Type: fax
   Number: 310 555-4567

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.

Listing 12-2 presents the source code to an application that demonstrates the reader approach to creating an object model and also writing this model.
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonWriter;
import static java.lang.System.*;
public class JSONPDemo
{
   public static void main(String[] args)
      throws FileNotFoundException
   {
      String json =
      "{" +
      "   "firstName": "John"," +
      "   "lastName": "Doe"," +
      "   "age": 42," +
      "   "address":" +
      "   {" +
      "      "street": "400 Some Street"," +
      "      "city": "Beverly Hills"," +
      "      "state": "CA"," +
      "      "zipcode": 90210" +
      "   }," +
      "   "phoneNumbers":" +
      "   [" +
      "      {" +
      "         "type": "home"," +
      "         "number": "310 555-1234"" +
      "      }," +
      "      {" +
      "         "type": "fax"," +
      "         "number": "310 555-4567"" +
      "      }" +
      "   ]" +
      "}";
      File file = new File("person.json") ;
      JsonReader reader =
         Json.createReader(file.exists() ?
                           new FileReader("person.json") :
                           new StringReader(json));
      JsonObject person = reader.readObject();
      out.printf("First name: %s%n",
                 person.getString("firstName"));
      out.printf("Last name: %s%n",
                 person.getString("lastName"));
      out.printf("Age: %d%n", person.getInt("age"));
      out.println("Address");
      out.println("-------");
      JsonObject address = person.getJsonObject("address");
      out.printf("   Street: %s%n",
                 address.getString("street"));
      out.printf("   City: %s%n",
                 address.getString("city"));
      out.printf("   State: %s%n",
                 address.getString("state"));
      out.printf("   Zipcode: %d%n",
                 address.getInt("zipcode")) ;
      out.println("Phone Numbers");
      out.println("-------------");
      JsonArray phoneNumbers =
         person.getJsonArray("phoneNumbers");
      for (JsonObject phoneNumber:
           phoneNumbers.getValuesAs(JsonObject.class))
      {
         out.printf("   Type: %s%n",
                    phoneNumber.getString("type"));
         out.printf("   Number: %s%n",
                    phoneNumber.getString("number"));
      }
      try (var fw = new FileWriter("person.json"))
      {
         JsonWriter writer = Json.createWriter(fw);
         writer.writeObject(person);
         writer.close();
      }
      catch (IOException ioe)
      {
         out.printf("I/O error: %s%n", ioe.getMessage());
      }
   }
}
Listing 12-2

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.

Listing 12-3 presents the source code to an application that demonstrates parsing JSON text.
import java.io.StringReader;
import javax.json.Json;
import javax.json.stream.JsonParser;
import static javax.json.stream.JsonParser.Event;
import static java.lang.System.*;
public class JSONPDemo
{
   public static void main(String[] args)
   {
      String json =
      "{" +
      "   "firstName": "John"," +
      "   "lastName": "Doe"," +
      "   "age": 42," +
      "   "address":" +
      "   {" +
      "      "street": "400 Some Street"," +
      "      "city": "Beverly Hills"," +
      "      "state": "CA"," +
      "      "zipcode": 90210" +
      "   }," +
      "   "phoneNumbers":" +
      "   [" +
      "      {" +
      "         "type": "home"," +
      "         "number": "310 555-1234"" +
      "      }," +
      "      {" +
      "         "type": "fax"," +
      "         "number": "310 555-4567"" +
      "      }" +
      "   ]" +
      "}";
      JsonParser parser =
         Json.createParser(new StringReader(json));
      while (parser.hasNext())
      {
         Event event = parser.next();
         if (event == Event.KEY_NAME)
            switch (parser.getString())
            {
               case "firstName":
                  parser.next();
                  out.printf("First name: %s%n",
                             parser.getString());
                  break;
               case "lastName":
                  parser.next();
                  out.printf("Last name: %s%n",
                             parser.getString());
                  break;
               case "age":
                  parser.next();
                  out.printf("Age: %d%n", parser.getInt());
                  break;
               case "address":
                  parser.next();
                  out.println("Address");
                  out.println("-------");
                  break;
               case "street":
                  parser.next();
                  out.printf("   Street: %s%n",
                             parser.getString());
                  break;
               case "city":
                  parser.next();
                  out.printf("   City: %s%n",
                             parser.getString());
                  break;
               case "state":
                  parser.next();
                  out.printf("   State: %s%n",
                             parser.getString());
                  break;
               case "zipcode":
                  parser.next();
                  out.printf("   Zipcode: %d%n",
                             parser.getInt());
                  break;
               case "phoneNumbers":
                  parser.next();
                  out.println("Phone Numbers");
                  out.println("-------------");
                  break;
               case "type":
                  parser.next();
                  out.printf("   Type: %s%n",
                             parser.getString());
                  parser.next();
                  parser.next();
                  out.printf("   Number: %s%n",
                             parser.getString());
                  break;
            }
      }
   }
}
Listing 12-3

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.

Listing 12-4 presents the source code to an application that shows how to generate JSON text.
import java.io.FileWriter;
import java.io.IOException;
import javax.json.Json;
import javax.json.stream.JsonGenerator;
import static java.lang.System.*;
public class JSONPDemo
{
   public static void main(String[] args)
   {
      try (var fw = new FileWriter("person.json"))
      {
         JsonGenerator generator = Json.createGenerator(fw);
         generator.writeStartObject()
                     .write("firstName", "John")
                     .write("lastName", "Doe")
                     .write("age", 42)
                     .writeStartObject("address")
                        .write("street", "400 Some Street")
                        .write("city", "Beverly Hills")
                        .write("state", "CA")
                        .write("zipcode", 90210)
                     .writeEnd()
                     .writeStartArray("phoneNumbers")
                        .writeStartObject()
                           .write("type", "home")
                           .write("number", "310 555-1234")
                        .writeEnd()
                        .writeStartObject()
                           .write("type", "fax")
                           .write("number", "310 555-4567")
                        .writeEnd()
                     .writeEnd()
                  .writeEnd();
         generator.close();
      }
      catch (IOException ioe)
      {
         out.printf("I/O error: %s%n", ioe.getMessage());
      }
   }
}
Listing 12-4

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).

A JSON Pointer must conform to the following ABNF ( http://en.wikipedia.org/wiki/Augmented_Backus-Naur_form ) syntax, or the pointer isn’t valid:
json-pointer    = *( "/" reference-token )
reference-token = *( unescaped / escaped )
unescaped       = %x00-2E / %x30-7D / %x7F-10FFFF
                  ; %x2F ('/') and %x7E ('~') are excluded from 'unescaped'
escaped         = "~" ( "0" / "1" )
                  ; representing '~' and '/', respectively

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.

Consider the following JSON document :
{
   "name": "Duke",
   "hobbies":
   [
      "reading",
      "surfing",
      "eating pizza"
   ]
}
The following list presents four example JSON Pointers for use with this document and (in round brackets) the values of these pointers:
  • "" (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.

Assuming that the previous JSON document has been created in memory (and assigned to duke), the following code fragment creates a JSON Pointer to the name reference token:
JsonPointer pointer = Json.createPointer("/name");
After a JsonPointer instance has been returned, invoking its JsonValue getValue(JsonStructure target) method returns the value at the referenced location in the specified target:
System.out.printf("%s%n%n",
                  pointer.getValue(duke)); // output: "Duke"
JsonPointer can be used to add items to, remove items from, and replace items in a JSON document. This interface provides the following methods for this purpose:
  • <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)

Listing 12-5 presents the source code to an application that demonstrates creating JsonPointers along with all of the previous JsonPointer methods.
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonPointer;
import static java.lang.System.*;
/*
   JsonPointer Demonstration:
   1) Create the following JSON document:
   {
      "firstName": "John",
      "lastName": "Doe",
      "age": 42,
      "address":
      {
         "streetAddress": "400 Some Street",
         "city": "Beverly Hills",
         "state": "CA",
         "zipcode": 90210
      },
      "phoneNumbers":
      [
         {
            "type": "home",
            "number": "310 555-1234"
         },
         {
            "type": "fax",
            "number": "310 555-4567"
         }
      ]
   }
   2) Use JsonPointer to convert the previous JSON document
      to the following JSON document :
   {
      "firstName": "John",
      "lastName": "Doe",
      "age": 30,
      "address":
      {
         "streetAddress": "400 Some Street",
         "city": "Beverly Hills",
         "state": "CA",
         "zipcode": 90210
      },
      "phoneNumbers":
      [
         {
            "type": "fax",
            "number": "310 555-4567"
         },
         {
            "type": "cell",
            "number": "123 456-7890"
         }
      ]
   }
*/
public class JSONPDemo
{
   public static void main(String[] args)
   {
      JsonObject person;
      person =
         Json.createObjectBuilder()
             .add("firstName", "John")
             .add("lastName", "Doe")
             .add("age", 42)
             .add("address", Json.createObjectBuilder()
                                 .add("street",
                                      "400 Some Street")
                                 .add("city",
                                      "Beverly Hills")
                                 .add("state", "CA")
                                 .add("zipcode", 90210))
             .add("phoneNumbers",
                  Json.createArrayBuilder()
                      .add(Json.createObjectBuilder()
                               .add("type", "home")
                               .add("number",
                                    "310 555-1234"))
                      .add(Json.createObjectBuilder()
                               .add("type", "fax")
                               .add("number",
                                    "310 555-4567")))
             .build();
      JsonPointer pointer = Json.createPointer("/age");
      person = pointer.replace(person,
                               Json.createValue(30));
      pointer = Json.createPointer("/phoneNumbers/-");
      person =
         pointer.add(person,
                     Json.createObjectBuilder()
                         .add("type", "cell")
                         .add("number",
                              "123 456-7890").build());
      pointer = Json.createPointer("/phoneNumbers/0");
      person = pointer.remove(person);
      out.printf("First name: %s%n",
                 person.getString("firstName"));
      out.printf("Last name: %s%n",
                 person.getString("lastName"));
      out.printf("Age: %d%n", person.getInt("age"));
      out.println("Address");
      out.println("-------");
      JsonObject address = person.getJsonObject("address");
      out.printf("   Street: %s%n",
                 address.getString("street"));
      out.printf("   City: %s%n",
                 address.getString("city"));
      out.printf("   State: %s%n",
                 address.getString("state"));
      out.printf("   Zipcode: %d%n",
                 address.getInt("zipcode"));
      out.println("Phone Numbers");
      out.println("-------------");
      JsonArray phoneNumbers =
         person.getJsonArray("phoneNumbers");
      for (JsonObject phoneNumber:
           phoneNumbers.getValuesAs(JsonObject.class))
      {
         out.printf("   Type: %s%n",
                    phoneNumber.getString("type"));
         out.printf("   Number: %s%n",
                    phoneNumber.getString("number"));
      }
   }
}
Listing 12-5

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.

Compile Listing 12-5 as follows:
javac -cp javax.json-api-1.1.3.jar;javax.json-1.1.3.jar JSONPDemo.java
Run the resulting application as follows:
java -cp javax.json-api-1.1.3.jar;javax.json-1.1.3.jar;. JSONPDemo
You should observe the following output:
First name: John
Last name: Doe
Age: 30
Address
-------
   Street: 400 Some Street
   City: Beverly Hills
   State: CA
   Zipcode: 90210
Phone Numbers
-------------
   Type: fax
   Number: 310 555-4567
   Type: cell
   Number: 123 456-7890
The Json class provides a pair of static methods for encoding (escaping) a JSON pointer string and for decoding (unescaping) an encoded pointer string:
  • String encodePointer(String pointer)

  • String decodePointer(String encPointer)

encodePointer() encodes ~ as ~0 and / as ~1. decodePointer() performs the opposite conversion. Listing 12-6 demonstrates both methods.
import javax.json.Json;
import static java.lang.System.*;
public class JSONPDemo
{
   public static void main(String[] args)
   {
      if (args.length != 1)
      {
         err.println("usage: java JSONPDemo pointer");
         return;
      }
      String encPointer = Json.encodePointer(args[0]);
      out.printf("Encoded pointer: %s%n ", encPointer);
      out.printf("Decoded pointer: %s%n ",
                 Json.decodePointer(encPointer));
   }
}
Listing 12-6

Demonstrating JSON Pointer Encoding and Decoding

Compile Listing 12-6 and then run the application as follows:
java -cp javax.json-1.1.3.jar;javax.json-api-1.1.3.jar;. JSONPDemo /a/b/c~d
You should observe the following output:
Encoded pointer: ~1a~1b~1c~0d
Decoded pointer: /a/b/c~d

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.

The JSON Patch document consists of an array of objects where each object denotes a single operation. Consider the following example, for use with JSON document {"a": {"b": {"c": "val", "d": "val", "e": "val"}}}:
[
  { "op": "test", "path": "/a/b/c", "value": "foo" },
  { "op": "remove", "path": "/a/b/c" },
  { "op": "add", "path": "/a/b/c",
    "value": [ "foo", "bar" ] },
  { "op": "replace", "path": "/a/b/c", "value": 42 },
  { "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
  { "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
]

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.

There are six operations: test, remove, add, replace, move, and copy :
  • 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.

JSON-P’s JsonPatch interface represents a JSON Patch. Obtain an instance of a class that implements this interface by executing Json’s JsonPatch createPatch(JsonArray patchArray) static method:
JsonArray patchops = ...  ;
JsonPatch patch = Json.createPatch(patchops);
After obtaining a JsonPatch instance, invoke its <T extends JsonStructure> T apply(t target) generic method to apply the specified patch operations to target:
JsonArray result = jsonpatch.apply(target);
Listing 12-7 presents the source code to an application that demonstrates creating a JsonPatch object and applying its operations to a JsonObject, to make the same document changes as demonstrated in Listing 12-5.
import java.io.StringReader;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import static java.lang.System.*;
/*
   JsonPatch Demonstration:
   1) Create the following JSON document:
   {
      "firstName": "John",
      "lastName": "Doe",
      "age": 42,
      "address":
      {
         "streetAddress": "400 Some Street",
         "city": "Beverly Hills",
         "state": "CA",
         "zipcode": 90210
      },
      "phoneNumbers":
      [
         {
            "type": "home",
            "number": "310 555-1234"
         },
         {
            "type": "fax",
            "number": "310 555-4567"
         }
      ]
   }
   2) Use JsonPatch to convert the previous JSON document
      to the following JSON document:
   {
      "firstName": "John",
      "lastName": "Doe",
      "age": 30,
      "address":
      {
         "streetAddress": "400 Some Street",
         "city": "Beverly Hills",
         "state": "CA",
         "zipcode": 90210
      },
      "phoneNumbers":
      [
         {
            "type": "fax",
            "number": "310 555-4567"
         },
         {
            "type": "cell",
            "number": "123 456-7890"
         }
      ]
   }
*/
public class JSONPDemo
{
   public static void main(String[] args)
   {
      JsonObject person;
      person =
         Json.createObjectBuilder()
             .add("firstName", "John")
             .add("lastName", "Doe")
             .add("age", 42)
             .add("address", Json.createObjectBuilder()
                                 .add("street",
                                      "400 Some Street")
                                 .add("city",
                                      "Beverly Hills")
                                 .add("state", "CA")
                                 .add("zipcode", 90210))
             .add("phoneNumbers",
                  Json.createArrayBuilder()
                      .add(Json.createObjectBuilder()
                               .add("type", "home")
                               .add("number",
                                    "310 555-1234"))
                      .add(Json.createObjectBuilder()
                               .add("type", "fax")
                               .add("number",
                                    "310 555-4567")))
             .build();
      String patch =
      "[" +
      "{" +
      ""op": "replace"," +
      ""path": "/age"," +
      ""value": 30" +
      "}," +
      "{" +
      ""op": "add"," +
      ""path": "/phoneNumbers/-"," +
      ""value": " +
      "{" +
      ""type": "cell"," +
      ""number": "123 456-7890"" +
      "}" +
      "}," +
      "{" +
      ""op": "remove"," +
      ""path": "/phoneNumbers/0"" +
      "}" +
      "]";
      JsonArray patchArray =
         Json.createReader(new StringReader(patch))
             .readArray();
      person = Json.createPatch(patchArray).apply(person);
      out.printf("First name: %s%n",
                 person.getString("firstName"));
      out.printf("Last name: %s%n",
                 person.getString("lastName"));
      out.printf("Age: %d%n", person.getInt("age"));
      out.println("Address");
      out.println("-------");
      JsonObject address = person.getJsonObject("address");
      out.printf("   Street: %s%n",
                 address.getString("street"));
      out.printf("   City: %s%n",
                 address.getString("city"));
      out.printf("   State: %s%n",
                 address.getString("state"));
      out.printf("   Zipcode: %d%n",
                 address.getInt("zipcode"));
      out.println("Phone Numbers");
      out.println("-------------");
      JsonArray phoneNumbers =
         person.getJsonArray("phoneNumbers");
      for (JsonObject phoneNumber:
           phoneNumbers.getValuesAs(JsonObject.class))
      {
         out.printf("   Type: %s%n",
                    phoneNumber.getString("type"));
         out.printf("   Number: %s%n",
                    phoneNumber.getString("number"));
      }
   }
}
Listing 12-7

Demonstrating JSON Patch

Compile Listing 12-7 and run the application. You should observe the following output:
First name: John
Last name: Doe
Age: 30
Address
-------
   Street: 400 Some Street
   City: Beverly Hills
   State: CA
   Zipcode: 90210
Phone Numbers
-------------
   Type: fax
   Number: 310 555-4567
   Type: cell
   Number: 123 456-7890
It can be tedious to specify all patch operations literally, which is why JSON-P also provides the JsonPatchBuilder interface for constructing a JSON Patch by adding JSON Patch operations incrementally. An instance of a class that implements this interface is obtained by executing either of Json’s createPatchBuilder() method s. The builder’s add(), remove(), and other methods are called to add patch operations. The build() method returns the resulting JsonPatch:
JsonPatchBuilder builder = Json.createPatchBuilder();
JsonPatch patch = builder.replace("/a/b/c", 42)
                         .copy("/a/b/e", "/a/b/d")
                         .build();
JSON-P provides a third way to obtain a JSON Patch. Json’s JsonPatch createDiff(JsonStructure source, JsonStructure target) static method generates a JSON Patch whose operations describe how to convert from source to target. Note that source and target must have the same types. In other words, they must both be JsonArrays or JsonObjects. The createDiff() method is demonstrated in Listing 12-8.
import javax.json.Json;
import javax.json.JsonObject;
import static java.lang.System.*;
public class JSONPDemo
{
   public static void main(String[] args)
   {
      JsonObject person1;
      person1 =
         Json.createObjectBuilder()
             .add("firstName", "John")
             .add("lastName", "Doe")
             .add("age", 42)
             .add("address", Json.createObjectBuilder()
                                 .add("street",
                                      "400 Some Street")
                                 .add("city",
                                      "Beverly Hills")
                                 .add("state", "CA")
                                 .add("zipcode", 90210))
             .add("phoneNumbers",
                  Json.createArrayBuilder()
                      .add(Json.createObjectBuilder()
                               .add("type", "home")
                               .add("number",
                                    "310 555-1234"))
                      .add(Json.createObjectBuilder()
                               .add("type", "fax")
                               .add("number",
                                    "310 555-4567")))
             .build();
      JsonObject person2;
      person2 =
         Json.createObjectBuilder()
             .add("firstName", "John")
             .add("lastName", "Doe")
             .add("age", 30)
             .add("address", Json.createObjectBuilder()
                                 .add("street",
                                      "400 Some Street")
                                 .add("city",
                                      "Beverly Hills")
                                 .add("state", "CA")
                                 .add("zipcode", 90210))
             .add("phoneNumbers",
                  Json.createArrayBuilder()
                      .add(Json.createObjectBuilder()
                               .add("type", "fax")
                               .add("number",
                                    "310 555-4567"))
                      .add(Json.createObjectBuilder()
                               .add("type", "cell")
                               .add("number",
                                    "123 456-7890")))
             .build();
      out.println(Json.createDiff(person1, person2));
   }
}
Listing 12-8

Creating a JSON Patch Based on Differences Between a Source and a Target

Compile Listing 12-8 and run the application. You should observe the following output:
[{"op":"replace","path":"/age","value":30},{"op":"remove","path":"/phoneNumbers/0"},{"op":"add","path":"/phoneNumbers/1","value":{"type":"cell","number":"123 456-7890"}}]

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 ).

For example, consider the following JSON document:
{
   "a": "b",
   "c":
   {
      "d": "e",
      "f": "g"
   }
}
The following JSON Merge Patch document specifies a change to a’s value and the removal of f :
{
   "a":"z",
   "c":
   {
      "f": null
   }
}
JSON-P’s JsonMergePatch interface represents a JSON Merge Patch. Obtain an instance of a class that implements this interface by executing Json’s JsonMergePatch createMergePatch(JsonValue patchArray) static method:
JsonValue patch = ...;
JsonMergePatch mergePatch = Json.createMergePatch(patch) ;
After obtaining a JsonMergePatch instance, invoke its JsonValue apply(JsonValue target) method to apply the specified patch to target:
JsonValue result = mergePatch.apply(target);
Listing 12-9 presents the source code to an application that demonstrates creating a JsonMergePatch object and applying it to a JsonObject, to make the same document changes as demonstrated in Listing 12-5.
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonMergePatch;
import javax.json.JsonObject;
import static java.lang.System.*;
public class JSONPDemo
{
   public static void main(String[] args)
   {
      JsonObject person;
      person =
         Json.createObjectBuilder()
             .add("firstName", "John")
             .add("lastName", "Doe")
             .add("age", 42)
             .add("address", Json.createObjectBuilder()
                                 .add("street",
                                      "400 Some Street")
                                 .add("city",
                                      "Beverly Hills")
                                 .add("state", "CA")
                                 .add("zipcode", 90210))
             .add("phoneNumbers",
                  Json.createArrayBuilder()
                      .add(Json.createObjectBuilder()
                               .add("type", "home")
                               .add("number",
                                    "310 555-1234"))
                      .add(Json.createObjectBuilder()
                               .add("type", "fax")
                               .add("number",
                                    "310 555-4567")))
             .build();
      JsonObject patch =
         Json.createObjectBuilder()
             .add("age", 30)
             .add("phoneNumbers",
                  Json.createArrayBuilder()
                      .add(Json.createObjectBuilder()
                               .add("type", "fax")
                               .add("number",
                                    "310 555-4567"))
                             .add(Json.createObjectBuilder()
                                      .add("type", "cell")
                                      .add("number",
                                           "123 456-7890")))
                  .build();
      JsonMergePatch mergePatch =
         Json.createMergePatch(patch);
      person = (JsonObject) mergePatch.apply(person);
      out.printf("First name: %s%n",
                 person.getString("firstName"));
      out.printf("Last name: %s%n",
                 person.getString("lastName"));
      out.printf("Age: %d%n", person.getInt("age"));
      out.println("Address");
      out.println("-------");
      JsonObject address = person.getJsonObject("address");
      out.printf("   Street: %s%n",
                 address.getString("street"));
      out.printf("   City: %s%n",
                 address.getString("city"));
      out.printf("   State: %s%n",
                 address.getString("state"));
      out.printf("   Zipcode: %d%n",
                 address.getInt("zipcode"));
      out.println("Phone Numbers");
      out.println("-------------");
      JsonArray phoneNumbers =
         person.getJsonArray("phoneNumbers");
      for (JsonObject phoneNumber:
           phoneNumbers.getValuesAs(JsonObject.class))
      {
         out.printf("   Type: %s%n",
                    phoneNumber.getString("type"));
         out.printf("   Number: %s%n",
                    phoneNumber.getString("number"));
      }
   }
}
Listing 12-9

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.

Compile Listing 12-9 and run the application. You should observe the following output:
First name: John
Last name: Doe
Age: 30
Address
-------
   Street: 400 Some Street
   City: Beverly Hills
   State: CA
   Zipcode: 90210
Phone Numbers
-------------
   Type: fax
   Number: 310 555-4567
   Type: cell
   Number: 123 456-7890
JSON-P provides another way to obtain a JSON Merge Patch. Json’s JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) static method generates a JSON Merge Patch from the source and the target, which yields the target when applied to the source—source and target don’t have to have the same JsonValue subtype. The createMergeDiff() method is demonstrated in Listing 12-10 .
import javax.json.Json;
import javax.json.JsonObject;
import static java.lang.System.*;
public class JSONPDemo
{
   public static void main(String[] args)
   {
      JsonObject person1;
      person1 =
         Json.createObjectBuilder()
             .add("firstName", "John")
             .add("lastName", "Doe")
             .add("age", 42)
             .add("address", Json.createObjectBuilder()
                                 .add("street",
                                      "400 Some Street")
                                 .add("city",
                                      "Beverly Hills")
                                 .add("state", "CA")
                                 .add("zipcode", 90210))
             .add("phoneNumbers",
                  Json.createArrayBuilder()
                      .add(Json.createObjectBuilder()
                               .add("type", "home")
                               .add("number",
                                    "310 555-1234"))
                      .add(Json.createObjectBuilder()
                               .add("type", "fax")
                               .add("number",
                                    "310 555-4567")))
             .build();
      JsonObject person2;
      person2 =
         Json.createObjectBuilder()
             .add("firstName", "John")
             .add("lastName", "Doe")
             .add("age", 30)
             .add("address", Json.createObjectBuilder()
                                 .add("street",
                                      "400 Some Street")
                                 .add("city",
                                      "Beverly Hills")
                                 .add("state", "CA")
                                 .add("zipcode", 90210))
             .add("phoneNumbers",
                  Json.createArrayBuilder()
                      .add(Json.createObjectBuilder()
                               .add("type", "fax")
                               .add("number",
                                    "310 555-4567"))
                      .add(Json.createObjectBuilder()
                               .add("type", "cell")
                               .add("number",
                                    "123 456-7890")))
             .build();
      out.println(Json.createMergeDiff(person1, person2)
                      .toJsonValue());
   }
}
Listing 12-10

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.

Compile Listing 12-10 and run the application. You should observe the following output:
{"age":30,"phoneNumbers":[{"type":"fax","number":"310 555-4567"},{"type":"cell","number":"123 456-7890"}]}

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.

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. Listing 12-11 presents the source code to an application that demonstrates some of these methods.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.json.Json;
import javax.json.JsonObject;
import static java.lang.System.*;
public class JSONPDemo
{
   public static void main(String[] args)
   {
      List<String> planets =
         Arrays.asList("Mercury", "Venus", "Terra", "Mars",
                       "Jupiter", "Saturn", "Uranus",
                       "Pluto");
      out.println(Json.createArrayBuilder(planets)
                      .remove(7)
                      .set(2, "Earth")
                      .add("Neptune")
                      .build());
      Map<String, Object> employees = new HashMap<>() ;
      employees.put("John Doe", 42);
      employees.put("Jill Smith", 38);
      employees.put("Bonnie Barnes", 26);
      JsonObject employees_ =
         Json.createObjectBuilder(employees)
             .remove("Jill Smith")
             .build();
      out.println(employees_);
   }
}
Listing 12-11

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.

Compile Listing 12-11 and run the application. You should observe the following output:
["Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"]
{"John Doe":42,"Bonnie Barnes":26}

Java SE 8 Support

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. For example, consider the following collector method:
Collector<JsonValue,JsonObjectBuilder,JsonObject>
   toJsonObject(Function<jsonValue,String> keyMapper,
                Function<JsonValue,JsonValue> valueMapper)

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.

Listing 12-12 presents the source code to an application that demonstrates this collector method.
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.stream.JsonCollectors;
import static java.lang.System.*;
public class JSONPDemo
{
   public static void main(String[] args)
   {
      JsonArray employees;
      employees = Json.createArrayBuilder()
                      .add(Json.createObjectBuilder()
                               .add("name", "John")
                               .add("age", 42))
                      .add(Json.createObjectBuilder()
                               .add("name", "Joan")
                               .add("age", 28))
                      .add(Json.createObjectBuilder()
                               .add("name", "Trevor")
                               .add("age", 35))
                      .add(Json.createObjectBuilder()
                               .add("name", "Sally")
                               .add("age", 49))
                      .add(Json.createObjectBuilder()
                               .add("name", "Frank")
                               .add("age", 26))
                      .build();
      out.println(employees);
      out.println();
      JsonObject result =
      employees.getValuesAs(JsonObject.class)
               .stream()
               .filter(x -> x.getInt("age") > 40)
               .collect(JsonCollectors.
                        toJsonObject(x ->
                                     x.asJsonObject()
                                      .getString("name"),
                                     x ->
                                     x.asJsonObject()
                                      .get("age")));
      out.println(result);
   }
}
Listing 12-12

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.

Compile Listing 12-12 and run the application. You should observe the following output:
[{"name":"John","age":42},{"name":"Joan","age":28},{"name":"Trevor","age":35},{"name":"Sally","age":49},{"name":"Frank","age":26}]
{"John":42,"Sally":49}

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.

The combination of JsonParser’s parsing methods along with getArrayStream() or getObjectStream() offers an efficient alternative to attempting to read a very large JSON document into memory (which might not fit). For example, suppose a public library contains a JSON document that itemizes all of its books in terms of ISBN, title, and state (available or loaned). Listing 12-13 presents a sample document .
[
   {
      "isbn": "1234567890",
      "title": "Sample Book 1",
      "state": "avail"
   },
   {
      "isbn": "2342340324",
      "title": "Sample Book 2",
      "state": "loaned"
   },
   {
      "isbn": "2342069340",
      "title": "Sample Book 3",
      "state": "avail"
   },
   {
      "isbn": "5940045033",
      "title": "Sample Book 4",
      "state": "avail"
   },
   {
      "isbn": "2394094699",
      "title": "Sample Book 5",
      "state": "loaned"
   },
   {
      "isbn": "3566433454",
      "title": "Sample Book 6",
      "state": "avail"
   },
   {
      "isbn": "6990349039",
      "title": "Sample Book 7",
      "state": "avail"
   },
   {
      "isbn": "5695695690",
      "title": "Sample Book 8",
      "state": "avail"
   }
]
Listing 12-13

A Sample Library JSON Document

Suppose it’s necessary to print the first five books whose state is avail(able). The entire document, which could grow very large, doesn’t need to be read into memory. It’s only necessary to read book objects one by one until the first five available books have been printed. Listing 12-14 presents the source code to an application that uses JsonParser along with getArrayStream() and various stream operations to accomplish this task.
import java.io.FileReader;
import java.io.FileNotFoundException;
import javax.json.Json;
import javax.json.stream.JsonParser;
import static java.lang.System.*;
public class JSONPDemo
{
   public static void main(String[] args)
      throws FileNotFoundException
   {
      if (args.length != 1)
      {
         err.println("usage: java JSONPDemo filespec");
         return;
      }
      JsonParser parser =
         Json.createParser(new FileReader(args[0]));
      while (parser.hasNext())
         if (parser.next() == JsonParser.Event.START_ARRAY)
         {
            parser.getArrayStream()
                  // convert to Stream<JsonObject>
                  .map(v -> v.asJsonObject())
                  .filter(obj -> obj.getString("state")
                                    .equals("avail"))
                  .limit(5)
                  .forEach(obj ->
                           out.printf("ISBN: %s, " +
                                      "TITLE: %s%n%n",
                                      obj.getString("isbn"),
                                   obj.getString("title")));
            // skip the rest of the JsonArray
            parser.skipArray();
         }
   }
}
Listing 12-14

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.

Compile Listing 12-14 and run the application. You should observe the following output:
ISBN: 1234567890, TITLE: Sample Book 1
ISBN: 2342069340, TITLE: Sample Book 3
ISBN: 5940045033, TITLE: Sample Book 4
ISBN: 3566433454, TITLE: Sample Book 6
ISBN: 6990349039, TITLE: Sample Book 7

Exercises

The following exercises are designed to test your understanding of Chapter 12’s content:
  1. 1.

    Define JSON-P.

     
  2. 2.

    Describe JSON-P 1.0’s package structure.

     
  3. 3.

    Identify the types for creating JsonArray and JsonObject models.

     
  4. 4.

    Which type is the superinterface of interface types that represent immutable JSON values?

     
  5. 5.

    In what ways does JSON-P 1.1 differ from JSON-P 1.0?

     
  6. 6.

    True or false: JSON-P 1.0’s streaming model is more flexible than its object model.

     
  7. 7.

    How do you construct a JsonObject model?

     
  8. 8.

    What types does the streaming model provide to read and write JSON content?

     
  9. 9.

    Describe JSON Merge Patch.

     
  10. 10.

    Identify the editing/transformation operations that have been added to JsonArrayBuilder and JsonObjectBuilder.

     
  11. 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. 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.17.50