Example Usage

The JSONPath2 library has several APIs available to perform JSONPath matching.

Syntax

XPath JSONPath Description
/ $ the root JSON value
. @ the current JSON value
/ . or [] child operator
// .. recursive descent (depth-first search)
* * wildcard (all elements of a JSON array; all values of a JSON object; otherwise none)
[] [] subscript operator
| [,] union operator (for two or more subscript operators)
n/a [start:end:step] slice operator (subset of elements of a JSON array)
[] ?() filter expression (for use with subscript operator)
JSONPath Filter Expression Description
$ or @ nested JSONPath (returns true if any match exists; otherwise, returns false)
=, !=, >=, <= >, < binary operator, where left- and right-hand operands are nested JSONPaths or JSON values (returns true if any match exists; otherwise, returns false)
and, or, not Boolean operator, where operands are JSONPath filter expressions
contains Checks if a string contains the specified substring (case-sensitive), or an array contains the specified element.
() parentheses

Functions

See #14 for more information.

The syntax for a function call is the name of the function followed by the arguments in parentheses, i.e., name(arg1, arg2, ..., argN), where the arguments are either JSONPaths or JSON values.

>>> s = '{"hello":"Hello, world!"}'
'{"hello":"Hello, world!"}'
>>> import json
>>> d = json.loads(s)
{'hello':'Hello, world!'}
>>> from jsonpath2.path import Path
>>> p = Path.parse_str('$["hello"][length()]')
<jsonpath2.path.Path object>
>>> [m.current_value for m in p.match(d)]
[13]
>>> [m.node.tojsonpath() for m in p.match(d)]
['$["hello"][length()]']
JavaScript Function Signature
Array.length length(): int
Array.prototype.entries entries(): List[Tuple[int, Any]]
Array.prototype.keys keys(): List[int]
Array.prototype.values values(): List[Any]
Object.entries entries(): List[Tuple[str, Any]]
Object.keys keys(): List[str]
Object.values values(): List[Any]
String.length length(): int
String.prototype.charAt charAt(index: int): str
String.prototype.substring substring(indexStart: int, indexEnd: Optional[int]): str

In the above table, the type aliases (Any, List, Optional and Tuple) are defined by the typing module from the Python Standard Library.

Examples

Some of the examples are provided by the test suite while some have been contributed via issues.

Test Suite Examples

Test the jsonpath module.

class bookstore_test.TestBookStore(methodName='runTest')[source]

Test the bookstore from original jsonpath article.

http://goessner.net/articles/JsonPath/

setUp()[source]

Setup the class.

test_bookstore_examples_1()[source]

Test the bookstore example 1.

>>> expr = Path.parse_str('$.store.book[*].author')
>>> expr.match(self.root_value)
test_bookstore_examples_10()[source]

Test the bookstore example 10.

>>> expr = Path.parse_str('$..book[*][?(@.author = "Nigel Rees")]')
>>> expr.match(self.root_value)
test_bookstore_examples_11()[source]

Test the bookstore example 11.

>>> expr = Path.parse_str('$..book[*][?(@.category != "fiction")]')
>>> expr.match(self.root_value)
test_bookstore_examples_12()[source]

Test the bookstore example 12.

>>> expr = Path.parse_str('$..book[*][?(@.price>=10)]')
>>> expr.match(self.root_value)
>>> expr = Path.parse_str('$..book[*][?(@.price>10)]')
>>> expr.match(self.root_value)
test_bookstore_examples_13()[source]

Test the bookstore example 13.

>>> expr = Path.parse_str('$..book[*][?(@.title contains "the")]')
>>> expr.match(self.root_value)
test_bookstore_examples_2()[source]

Test the bookstore example 2.

>>> expr = Path.parse_str('$..author')
>>> expr.match(self.root_value)
test_bookstore_examples_3()[source]

Test the bookstore example 3.

>>> expr = Path.parse_str('$.store.*')
>>> expr.match(self.root_value)
test_bookstore_examples_4()[source]

Test the bookstore example 4.

>>> expr = Path.parse_str('$.store..price')
>>> expr.match(self.root_value)
test_bookstore_examples_5()[source]

Test the bookstore example 5.

>>> expr = Path.parse_str('$..book[2]')
>>> expr.match(self.root_value)
test_bookstore_examples_6()[source]

Test the bookstore example 6.

>>> expr = Path.parse_str('$..book[-1:]')
>>> expr.match(self.root_value)
>>> expr = Path.parse_str('$..book[-1]')
>>> expr.match(self.root_value)
>>> expr = Path.parse_str('$..book[3:4:1]')
>>> expr.match(self.root_value)
test_bookstore_examples_7()[source]

Test the bookstore example 7.

>>> expr = Path.parse_str('$..book[0,1]')
>>> expr.match(self.root_value)
>>> expr = Path.parse_str('$..book[:2]')
>>> expr.match(self.root_value)
>>> expr = Path.parse_str('$..book[:2:1]')
>>> expr.match(self.root_value)
test_bookstore_examples_8()[source]

Test the bookstore example 8.

>>> expr = Path.parse_str('$..book[*][?(@.isbn)]')
>>> expr.match(self.root_value)
test_bookstore_examples_9()[source]

Test the bookstore example 9.

>>> expr = Path.parse_str('$..book[*][?(@.price<=10)]')
>>> expr.match(self.root_value)
>>> expr = Path.parse_str('$..book[*][?(@.price<10)]')
>>> expr.match(self.root_value)
class bookstore_test.TestExtendedBookStore(methodName='runTest')[source]

This test extends the standard bookstore test for completness.

setUp()[source]

Copy the original bookstore document to this class.

test_bookstore_extexample_1()[source]

Test the bookstore example with step function.

>>> expr = Path.parse_str('$..book[::2]')
>>> expr.match(self.root_value)
test_bookstore_extexamples_2()[source]

Test the bookstore example slice with end and multiple colons.

>>> expr = Path.parse_str('$..book[:2:]')
>>> expr.match(self.root_value)
test_bookstore_extexamples_3()[source]

Test the bookstore example slice with start and multiple colons.

>>> expr = Path.parse_str('$..book[2::]')
>>> expr.match(self.root_value)

Issue Examples

Issue #19

This issue involved finding the full path to the matched attribute.

The result isn’t strictly supported by the library but code examples are provided.

import json
import typing

from jsonpath2.node import Node
from jsonpath2.nodes.root import RootNode
from jsonpath2.nodes.subscript import SubscriptNode
from jsonpath2.nodes.terminal import TerminalNode
from jsonpath2.path import Path
from jsonpath2.subscript import Subscript

data = json.loads("""
{
    "values": [
        {"type": 1, "value": 2},
        {"type": 2, "value": 3},
        {"type": 1, "value": 10}
    ]
}
""")

path = Path.parse_str("$.values.*[?(@.type = 1)].value")

def get_subscripts(node: Node) -> typing.List[typing.List[Subscript]]:
    return get_subscripts_(node, [])

def get_subscripts_(node: Node, accumulator: typing.List[typing.List[Subscript]]) -> typing.List[typing.List[Subscript]]:
    if isinstance(node, RootNode):
        return get_subscripts_(node.next_node, accumulator)
    elif isinstance(node, SubscriptNode):
        accumulator.append(node.subscripts)
        return get_subscripts_(node.next_node, accumulator)
    elif isinstance(node, TerminalNode):
        return accumulator

for match_data in path.match(data):
    print(f"Value: {match_data.current_value}")
    print(f"JSONPath: {match_data.node.tojsonpath()}")
    print(f"Subscripts: {get_subscripts(match_data.node)}")
    print("")

The snippet above iterates over the match results, prints the value and JSONPath and then prints the list of subscripts. The list of subscripts is constructed by traversing the structure of the abstract syntax tree for the JSONPath.

The results [modulo the memory addresses] are:

Value: 2
JSONPath: $["values"][0]["value"]
Subscripts: [[<jsonpath2.subscripts.objectindex.ObjectIndexSubscript object at 0x10f6a3278>], [<jsonpath2.subscripts.arrayindex.ArrayIndexSubscript object at 0x10f6a37b8>], [<jsonpath2.subscripts.objectindex.ObjectIndexSubscript object at 0x10f6a3390>]]

Value: 10
JSONPath: $["values"][2]["value"]
Subscripts: [[<jsonpath2.subscripts.objectindex.ObjectIndexSubscript object at 0x10f6a3278>], [<jsonpath2.subscripts.arrayindex.ArrayIndexSubscript object at 0x10f6a3978>], [<jsonpath2.subscripts.objectindex.ObjectIndexSubscript object at 0x10f6a3390>]]

The first subscript is the "values" key. The second subscript is the index of the {"type":"value"} object. The third subscript is the "value" key.

Note that the result (the list of subscripts) is a list of lists. This is because instances of the SubscriptNode class are constructed using zero or more instances of the Subscript class.

Grammar and parser

The ANTLR v4 grammar for JSONPath is available at jsonpath2/parser/JSONPath.g4.

Installing ANTLR v4

Adapted from antlr docs.

cd /usr/local/lib
curl -O https://www.antlr.org/download/antlr-4.10.1-complete.jar

export CLASSPATH=".:/usr/local/lib/antlr-4.10.1-complete.jar:$CLASSPATH"

alias antlr4='java -Xmx500M -cp "/usr/local/lib/antlr-4.10.1-complete.jar:$CLASSPATH" org.antlr.v4.Tool'
alias grun='java org.antlr.v4.gui.TestRig'

Building the parser for the grammar

Adapted from antlr docs.

antlr4 -Dlanguage=Python3 -o . -lib . jsonpath2/parser/JSONPath.g4