Welcome to JSONPath2 documentation!¶
This repository contains an implementation of JSONPath XPath for JSON for the Python programming language.
Installation¶
The JSONPath2 library is available through PyPi so creating a virtual environment to install is what is shown below. Please keep in mind compatibility with the Pacifica Core services.
Installation in Virtual Environment¶
These installation instructions are intended to work on both Windows, Linux, and Mac platforms. Please keep that in mind when following the instructions.
Please install the appropriate tested version of Python for maximum chance of success.
Linux and Mac Installation¶
mkdir ~/.virtualenvs
python -m virtualenv ~/.virtualenvs/pacifica
. ~/.virtualenvs/pacifica/bin/activate
pip install jsonpath2
Windows Installation¶
This is done using PowerShell. Please do not use Batch Command.
mkdir "$Env:LOCALAPPDATA\virtualenvs"
python.exe -m virtualenv "$Env:LOCALAPPDATA\virtualenvs\pacifica"
& "$Env:LOCALAPPDATA\virtualenvs\pacifica\Scripts\activate.ps1"
pip install jsonpath2
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)
-
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
Python JSONPath2 Module¶
Python JSONPath2 Expressions Module¶
Expressions used in jsonpath module.
The operator expression module.
-
class
jsonpath2.expressions.operator.
AndVariadicOperatorExpression
(*args, **kwargs)[source]¶ The boolean ‘and’ operator expression.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.expressions.operator.
BinaryOperatorExpression
(token: str, callback: Callable[[object, object], bool], left_node_or_value: Union[jsonpath2.node.Node, object], right_node_or_value: Union[jsonpath2.node.Node, object])[source]¶ Binary operator expression.
-
__init__
(token: str, callback: Callable[[object, object], bool], left_node_or_value: Union[jsonpath2.node.Node, object], right_node_or_value: Union[jsonpath2.node.Node, object])[source]¶ Constructor save the left right and token.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.expressions.operator.
EqualBinaryOperatorExpression
(*args, **kwargs)[source]¶ Binary Equal operator expression.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.expressions.operator.
GreaterThanBinaryOperatorExpression
(*args, **kwargs)[source]¶ Expression to handle greater than.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.expressions.operator.
GreaterThanOrEqualToBinaryOperatorExpression
(*args, **kwargs)[source]¶ Expression to handle greater than or equal.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.expressions.operator.
LessThanBinaryOperatorExpression
(*args, **kwargs)[source]¶ Expression to handle less than.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.expressions.operator.
LessThanOrEqualToBinaryOperatorExpression
(*args, **kwargs)[source]¶ Expression to handle less than or equal.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.expressions.operator.
NotEqualBinaryOperatorExpression
(*args, **kwargs)[source]¶ Binary Equal operator expression.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.expressions.operator.
NotUnaryOperatorExpression
(*args, **kwargs)[source]¶ Unary class to handle the ‘not’ expression.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.expressions.operator.
OperatorExpression
[source]¶ Basic operator expression object.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.expressions.operator.
OrVariadicOperatorExpression
(*args, **kwargs)[source]¶ The boolean ‘or’ operator expression.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.expressions.operator.
UnaryOperatorExpression
(token: str, callback: Callable[[bool], bool], expression: jsonpath2.expression.Expression)[source]¶ Unary operator expression base class.
-
__init__
(token: str, callback: Callable[[bool], bool], expression: jsonpath2.expression.Expression)[source]¶ Save the callback operator the token and expression.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.expressions.operator.
VariadicOperatorExpression
(token: str, callback: Callable[[List[bool]], bool], expressions: List[jsonpath2.expression.Expression] = None)[source]¶ Base class to handle boolean expressions of variadic type.
-
__init__
(token: str, callback: Callable[[List[bool]], bool], expressions: List[jsonpath2.expression.Expression] = None)[source]¶ Save the operator token, callback and the list of expressions.
-
_abc_impl
= <_abc_data object>¶
-
Some expression module.
Python JSONPath2 Nodes Module¶
Nodes module contains all the node definitions.
The current node module.
-
class
jsonpath2.nodes.current.
CurrentNode
(next_node: jsonpath2.node.Node)[source]¶ Current node class to store current node info.
-
_abc_impl
= <_abc_data object>¶
-
Recursive descent module.
-
class
jsonpath2.nodes.recursivedescent.
RecursiveDescentNode
(next_node: jsonpath2.node.Node)[source]¶ Recursive descent node class.
-
_abc_impl
= <_abc_data object>¶
-
Root node type.
-
class
jsonpath2.nodes.root.
RootNode
(next_node: jsonpath2.node.Node)[source]¶ Root node to start the process.
-
_abc_impl
= <_abc_data object>¶
-
The subscript module.
-
class
jsonpath2.nodes.subscript.
SubscriptNode
(next_node: jsonpath2.node.Node, subscripts: List[jsonpath2.subscript.Subscript] = None)[source]¶ The subscript node class to handle ‘[]’.
-
__init__
(next_node: jsonpath2.node.Node, subscripts: List[jsonpath2.subscript.Subscript] = None)[source]¶ Save the next node and subscripts.
-
_abc_impl
= <_abc_data object>¶
-
Terminal node object.
Python JSONPath2 Parser Module¶
The jsonpath parser module.
-
exception
jsonpath2.parser.
CallableSubscriptNotFoundError
(name)[source]¶ Callable subscript not found error.
-
class
jsonpath2.parser.
_JSONPathListener
(_stack=None)[source]¶ -
-
exitAndExpression
(ctx: jsonpath2.parser.JSONPathParser.JSONPathParser.AndExpressionContext)[source]¶
-
exitNotExpression
(ctx: jsonpath2.parser.JSONPathParser.JSONPathParser.NotExpressionContext)[source]¶
-
exitSubscriptable
(ctx: jsonpath2.parser.JSONPathParser.JSONPathParser.SubscriptableContext)[source]¶
-
exitSubscriptableArguments
(ctx: jsonpath2.parser.JSONPathParser.JSONPathParser.SubscriptableArgumentsContext)[source]¶
-
exitSubscriptableBareword
(ctx: jsonpath2.parser.JSONPathParser.JSONPathParser.SubscriptableBarewordContext)[source]¶
-
-
class
jsonpath2.parser.
_JSONPathParser
(input: antlr4.BufferedTokenStream.TokenStream, output: TextIO = <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>)[source]¶
-
jsonpath2.parser.
_createCallableSubscript
(name, *args, **kwargs)[source]¶ Create callable subscript for name, arguments and keyword arguments.
-
jsonpath2.parser.
_parse_input_stream
(input_stream: antlr4.InputStream.InputStream) → jsonpath2.nodes.root.RootNode[source]¶
Python JSONPath2 Subscripts Module¶
Subscripts module contains the various subscripting classes.
Array Index subscript of the parse tree.
-
class
jsonpath2.subscripts.arrayindex.
ArrayIndexSubscript
(index: int)[source]¶ Array index subscript object.
-
_abc_impl
= <_abc_data object>¶
-
Array slicing module.
-
class
jsonpath2.subscripts.arrayslice.
ArraySliceSubscript
(start: int = None, end: int = None, step: int = None)[source]¶ Array slice class for the parse tree.
-
__init__
(start: int = None, end: int = None, step: int = None)[source]¶ Save the start end and step in the array slice.
-
_abc_impl
= <_abc_data object>¶
-
Callable subscript.
-
class
jsonpath2.subscripts.callable.
CallableSubscript
(*args)[source]¶ Callable subscript object.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.subscripts.callable.
CharAtCallableSubscript
(*args)[source]¶ charAt(int) callable subscript object.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.subscripts.callable.
EntriesCallableSubscript
(*args)[source]¶ entries() callable subscript object.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.subscripts.callable.
KeysCallableSubscript
(*args)[source]¶ keys() callable subscript object.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.subscripts.callable.
LengthCallableSubscript
(*args)[source]¶ length() callable subscript object.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.subscripts.callable.
SubstringCallableSubscript
(*args)[source]¶ substring(int[, int]) callable subscript object.
-
_abc_impl
= <_abc_data object>¶
-
-
class
jsonpath2.subscripts.callable.
ValuesCallableSubscript
(*args)[source]¶ values() callable subscript object.
-
_abc_impl
= <_abc_data object>¶
-
Filter parse tree.
-
class
jsonpath2.subscripts.filter.
FilterSubscript
(expression: jsonpath2.expression.Expression)[source]¶ Filter subscript in the parse tree.
-
_abc_impl
= <_abc_data object>¶
-
Node.
-
class
jsonpath2.subscripts.node.
NodeSubscript
(next_node: jsonpath2.node.Node)[source]¶ Node subscript in the parse tree.
-
_abc_impl
= <_abc_data object>¶
-
Object index subscript module.
-
class
jsonpath2.subscripts.objectindex.
ObjectIndexSubscript
(index: str)[source]¶ Object index subscript part of the jsonpath parse tree.
-
_abc_impl
= <_abc_data object>¶
-
Wild cart subscript module.
The jsonpath2 module.
-
jsonpath2.
match
(path_str: str, root_value: object) → Generator[jsonpath2.node.MatchData, None, None][source]¶ Match root value of the path.
The
jsonpath2.match
function is a shortcut to match a given JSON data structure against a JSONPath string.>>> import jsonpath2 >>> doc = {'hello': 'Hello, world!'} >>> [x.current_value for x in jsonpath2.match('$.hello', doc)] ['Hello, world!']
Expression module.
-
class
jsonpath2.expression.
Expression
[source]¶ Add the expression methods to the jsonpath object.
-
_abc_impl
= <_abc_data object>¶
-
The parse tree node module.
-
class
jsonpath2.node.
MatchData
(node, root_value, current_value)[source]¶ Match data object for storing node values.
The
jsonpath2.node.MatchData
class represents the JSON value and context for a JSONPath match.This class is constructed with respect to a root JSON value, a current JSON value, and an abstract syntax tree node.
- Attributes:
root_value
The root JSON value.current_value
The current JSON value (i.e., the matching JSON value).node
The abstract syntax tree node.
-
class
jsonpath2.node.
Node
[source]¶ Node object for the jsonpath parsetree.
The
jsonpath2.node.Node
class represents the abstract syntax tree for a JSONPath.-
_abc_impl
= <_abc_data object>¶
-
match
(root_value: object, current_value: object) → Generator[jsonpath2.node.MatchData, None, None][source]¶ Abstract method to determine a node match.
Match the given root and current JSON data structures against this instance. For each match, yield an instance of the
jsonpath2.node.MatchData
class.
-
The path module.
-
class
jsonpath2.path.
Path
(root_node: jsonpath2.nodes.root.RootNode)[source]¶ Path parsetree object.
The
jsonpath2.path.Path
class represents a JSONPath.>>> 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"]') <jsonpath2.path.Path object> >>> [match_data.current_value for match_data in p.match(d)] ['Hello, world!'] >>> [match_data.node.tojsonpath() for match_data in p.match(d)] ['$["hello"]']
This class is constructed with respect to the given instance of the
jsonpath2.nodes.root.RootNode
class (viz., theroot_node
property).- Attributes:
root_node
The root node of the abstract syntax tree for this instance.
-
match
(root_value: object) → Generator[jsonpath2.node.MatchData, None, None][source]¶ Match root value of the path.
Match the given JSON data structure against this instance. For each match, yield an instance of the
jsonpath2.node.MatchData
class.
The Subscript module.
-
class
jsonpath2.subscript.
Subscript
[source]¶ Subscript has no value beyond a node other than type.
-
_abc_impl
= <_abc_data object>¶
-
A JSONPath abstract class.