XPath is used to extract values from XML documents. JsonPath performs this task for JSON documents. Chapter 10 introduces you to JsonPath.
Note
If you’re unfamiliar with XPath, I recommend that you read Chapter 5 before reading this chapter. JsonPath was derived from XPath.
What Is JsonPath?
JsonPath is a declarative query language (also known as a path-expression-syntax) for selecting and extracting a JSON document’s property values. For example, you can use JsonPath to locate "John" in {"firstName": "John"} and return this value. JsonPath is based on XPath 1.0.
JsonPath was created by Stefan Goessner ( http://goessner.net ). Goessner also created JavaScript-based and PHP-based implementations of JsonPath. For complete documentation, check out Goessner’s website ( http://goessner.net/articles/JsonPath/index.html ).
Swedish software company Jayway ( www.jayway.com ) subsequently adapted JsonPath to Java. Their Java version of JsonPath is the focus of this chapter. You will find complete documentation on Jayway’s implementation of JsonPath at http://github.com/jayway/JsonPath .
Learning the JsonPath Language
JsonPath is a simple language with various features that are similar to their XPath counterparts. This language is used to construct path expressions.
The $ character represents the anonymous root JSON object. The leftmost dot character separates the object root from the phoneNumbers property name. The [0] syntax identifies the first element in the array assigned to phoneNumbers.
The first array element stores an anonymous object consisting of "type": "home" and "number": "212 555-1234" properties. The rightmost dot character accesses this object’s number child property name, which is assigned the value 212 555-1234. This value is returned from the expression.
JsonPath Basic Operators
Operator | Description |
---|---|
$ | The root element to query. This operator starts all path expressions. It’s equivalent to XPath’s / symbol. |
@ | The current node being processed by a filter predicate. It’s equivalent to XPath’s . symbol. |
* | Wildcard. Available anywhere a name or numeric value is required. |
.. | Deep scan (also known as recursive descent). Available anywhere a name is required. It’s equivalent to XPath’s // symbol. |
. name | Dot-notated child. The dot is equivalent to XPath’s / symbol. |
[' name ' (, ' name ')] | Bracket-notated child or children. |
[ number (, number )] | Array index or indexes. |
[ start : end ] | Array slice operator. |
[?( expression )] | Filter operator. The expression must evaluate to a Boolean value. In other words, it’s a predicate. |
JsonPath Functions
Function | Description |
---|---|
min() | Return the minimum value (as a double) in an array of numbers. |
max() | Return the maximum value (as a double) in an array of numbers. |
avg() | Return the average value (as a double) of an array of numbers. |
stddev() | Return the standard deviation value (as a double) of an array of numbers. |
length() | Return the length (as an int) of an array. |
JsonPath Filter Operators
Operator | Description |
---|---|
== | Return true when the left operand equals the right operand. Note that 1 is not equal to '1' (i.e., number 1 and string 1 are two different things). |
!= | Return true when the left operand doesn’t equal the right operand. |
< | Return true when the left operand is less than the right operand. |
<= | Return true when the left operand is less than or equal to the right operand. |
> | Return true when the left operand is greater than the right operand. |
>= | Return true when the left operand is greater than or equal to the right operand. |
=~ | Return true when the left operand matches the regular expression specified by the right operand; for example, [?(@.name =~ /foo.*?/i)]. |
in | Return true when the left operand exists in the right operand; for example, [?(@.grade in ['A', 'B'])]. |
nin | Return true when the left operand doesn’t exist in the right operand. |
subsetof | Return true when the left operand (an array) is a subset of the right operand (an array); for example, [?(@.sizes subsetof ['S', 'M', 'L'])]. |
size | Return true when the size of the left operand (an array or string) matches the right operand (an integer). |
empty | Return true when the left operand (an array or string) is empty and the right operand is true , or return true when the left operand is not empty and the right operand is false. |
Within a predicate, you must enclose any string literals with single or double quotes, which both examples demonstrate.
Obtaining and Using the JsonPath Library
As with Chapter 8’s mJson and Chapter 9’s Gson, you can obtain JsonPath from the Central Maven Repository ( http://search.maven.org ).
Note
If you’re unfamiliar with Maven, think of it as a build tool for Java projects, although Maven developers think of Maven as more than just a build tool—see http://maven.apache.org/background/philosophy-of-maven.html .
This XML fragment reveals 2.4.0 as the version of Jayway’s JsonPath library that I’m using in this chapter.
Note
It’s common for Maven projects to be dependent on other projects. For example, the mJson project ( http://search.maven.org/artifact/org.sharegov/mjson/1.4.0/bundle ) that I discussed in Chapter 8 and the Gson project ( http://search.maven.org/artifact/com.google.code.gson/gson/2.8.5/jar ) that I discussed in Chapter 9 are dependent on JUnit ( http://en.wikipedia.org/wiki/JUnit ). I didn’t mention or discuss downloading JUnit in either chapter because this library isn’t required for normal use.
Because I’m not currently using Maven, I downloaded the JsonPath JAR file and all of the JAR files on which JsonPath normally depends and then added all of these JAR files to my CLASSPATH. The easiest way for me to accomplish the download task was to point my browser to https://jar-download.com/artifacts/com.jayway.jsonpath/json-path/2.4.0/source-code and click the “Download json-path (2.4.0)” button link.
accessors-smart-1.2.jar
asm-5.0.4.jar
json-path-2.4.0.jar
json-smart-2.3.jar
slf4j-api-1.7.25.jar
Note
Jayway JsonPath is licensed according to Apache License Version 2.0 ( www.apache.org/licenses/ ).
These command lines assume that the JAR files are located in the current directory. To facilitate working with the command lines, place them in a pair of batch files on Windows platforms (substitute %1 for source file or main classfile) or their counterparts on other platforms.
Note
JsonPath 2.4.0’s API reference is available online at www.javadoc.io/doc/com.jayway.jsonpath/json-path/2.4.0 .
Exploring the JsonPath Library
The JsonPath library is organized into several packages. You will typically interact with the com.jayway.jsonpath package and its types. In this section, I focus exclusively on this package while showing you how to extract values from JSON objects and use predicates to filter items.
Extracting Values from JSON Objects
A First Taste of JsonPath
The Predicate varargs list lets you specify an array of filter predicates to match filter predicate place holders (identified as ? characters) in the jsonPath string. I’ll demonstrate Predicate and related types later in this chapter.
This generic method is called on the previously compiled JsonPath instance. It receives the string-based JSON object (assigned to json) as its argument and applies the JsonPath expression in the compiled JsonPath instance to this argument. The result is the JSON object identified by $.store.book[1].
The read() method is generic because it can return one of several types. In this example, it returns an instance of the java.util.LinkedHashMap class (a subclass of java.util.Hashmap) for storing JSON object property names and their values.
This method creates a new JsonPath object for the jsonPath argument and applies it to the json string. I ignore filters in the example.
The JsonPath expression passed to jsonPath is $.store.book[*].author. This expression includes the * wildcard to match all elements in the book array. It returns the value of the author property for each element in this array.
read() returns this value as an instance of the net.minidev.json.JSONArray class, which is stored in the json-smart-2.3.jar file that you must include in the CLASSPATH. Because JSONArray extends java.util.ArrayList<Object>, it’s legal to cast the returned object to List<Object>.
To further demonstrate read(), main() lastly invokes this method with JsonPath expression $.store.book[1].author, which returns the value of the author property in the anonymous object stored in the second element of the book array. This time, read() returns a java.lang.String object.
Note
Regarding the generic read() methods, JsonPath automatically attempts to cast the result to the type that the method’s invoker expects, such as a hashmap for a JSON object, a list of objects for a JSON array, and a string for a JSON string.
You’ll probably also observe some messages about SLF4J (Simple Logging Facade for Java) not being able to load the StaticLoggerBinder class and defaulting to a no-operation logger implementation. You can safely ignore these messages.
Using Predicates to Filter Items
JsonPath supports filters for restricting the nodes that are extracted from a JSON document to those that match the criteria specified by predicates (Boolean expressions). You can work with inline predicates, filter predicates, or custom predicates.
Inline Predicates
Demonstrating Inline Predicates
$.store.book[?(@.isbn)].title: returns the title values for all book elements that contain an isbn property
$.store.book[?(@.category == 'fiction')].title: returns the title values for all book elements whose category property is assigned the string value fiction
$..book[?(@.author =~ /.*REES/i)].title: returns the title values for all book elements whose author property value ends with rees (case is insignificant)
$..book[?(@.price >= 10 && @.price <= 20)].title: returns the title values for all book elements whose price property value lies between 10 and 20
$..book[?(@.author in ['Nigel Rees'])].title: returns the title values for all book elements whose author property value matches Nigel Rees
$..book[?(@.author nin ['Nigel Rees'])].title: returns the title values for all book elements whose author property value doesn’t match Nigel Rees
$.store.bicycle[?(@.accessories subsetof ['horn', 'bottle', 'light'])].price: returns the price values for all bicycle elements whose accessories property value (an array) is a subset of the ['horn', 'bottle', 'light'] array
$.store.bicycle[?(@.accessories subsetof ['horn', 'bottle'])].price: returns the price values for all bicycle elements whose accessories property value (an array) is a subset of the ['horn', 'bottle'] array
$..book[?(@.author size 12)].title: returns the title values for all book elements whose author property value (a string) is exactly 12 characters long
$..book[?(@.author size 13)].title: returns the title values for all book elements whose author property value (a string) is exactly 13 characters long
$..bicycle[?(@.accessories empty true)].price: returns the price values for all bicycle elements whose accessories property value (an array) is empty
$..bicycle[?(@.accessories empty false)].price: returns the price values for all bicycle elements whose accessories property value (an array) is not empty
$..book[?(@.category empty true)].title: returns the title values for all book elements whose category property value (a string) has zero length
Filter Predicates
A filter predicate is a predicate expressed as an instance of the abstract Filter class, which implements the Predicate interface.
Criteria’s Criteria where(String key) static method returns a Criteria object that stores the provided key, which is price in this example. Its Criteria lt(Object o) method returns a Criteria object for the < operator that identifies the value that’s compared to the value of the key.
Note
When multiple filter predicates are provided, they are applied in left-to-right order of the placeholders where the number of placeholders must match the number of provided filter predicates. You can specify multiple predicate placeholders in one filter operation [?, ?]; both predicates must match.
For each book element, the read() method executes the filter predicate when it detects the ? placeholder in the JsonPath expression.
Demonstrating Filter Predicates
Custom Predicates
A custom predicate is a predicate created from a class that implements the Predicate interface.
PredicateContext is a nested interface whose methods provide information about the context in which apply() is called. For example, Object root() returns a reference to the entire JSON document, and Object item() returns the current item being evaluated by this predicate.
apply() returns the predicate value: true (item is accepted) or false (item is rejected).
PredicateContext’s <T> T item(java.lang.Class<T> clazz) generic method maps the JSON object in the Book element to a java.util.Map.
For each book element, read() executes the custom predicate associated with the ? and returns a list of maps (one map per accepted item).
Demonstrating Custom Predicates
Exercises
- 1.
Define JsonPath.
- 2.
True or false: JsonPath is based on XPath 2.0.
- 3.
Identify the operator that represents the root JSON object.
- 4.
In what notations can you specify JsonPath expressions?
- 5.
What operator represents the current node being processed by a filter predicate?
- 6.
True or false: JsonPath’s deep scan operator (..) is equivalent to XPath’s / symbol.
- 7.
What does JsonPath’s JsonPath compile(String jsonPath, Predicate... filters) static method accomplish?
- 8.
What is the return type of the <T> T read(String json) generic method that returns JSON object property names and their values?
- 9.
Identify the three predicate categories.
- 10.
Given JSON object { "number": [10, 20, 25, 30] }, write a JsonPathDemo application that extracts and outputs the maximum (30), minimum (10), and average (21.25) values.
Summary
JsonPath is a declarative query language (also known as a path-expression-syntax) for selecting and extracting a JSON document’s property values.
JsonPath is a simple language with various features that are similar to their XPath counterparts. This language is used to construct path expressions. Each expression begins with the $ operator, which identifies the root element of the query and which corresponds to the XPath / symbol.
As with Chapter 8’s mJson and Chapter 9’s Gson, you can obtain JsonPath from the Central Maven Repository. Alternatively, if you’re not using Maven, you can download the JsonPath JAR file and all of the JAR files on which JsonPath normally depends and then add all of these JAR files to your CLASSPATH.
The JsonPath library is organized into several packages. You will typically interact with the com.jayway.jsonpath package and its types. In this chapter, you focused exclusively on this package while learning how to extract values from JSON objects and use predicates to filter items.
Chapter 11 introduces Jackson for parsing and generating JSON content.