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 |
( … ) |
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/
-
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_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)
-
-
class
bookstore_test.
TestExtendedBookStore
(methodName='runTest')[source]¶ This test extends the standard bookstore test for completness.
-
test_bookstore_extexample_1
()[source]¶ Test the bookstore example with step function.
>>> 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 http://www.antlr.org/download/antlr-4.7.1-complete.jar
export CLASSPATH=".:/usr/local/lib/antlr-4.7.1-complete.jar:$CLASSPATH"
alias antlr4='java -Xmx500M -cp "/usr/local/lib/antlr-4.7.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