The JSONPath2 library has several APIs available to perform JSONPath matching.
+--------+----------------------+-----------------------------------+
| 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 | Description |
| Expression | |
+===================+===============================================+
| ``$`` 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``, | Boolean operator, where operands are JSONPath |
| ``not`` | filter expressions |
+-------------------+-----------------------------------------------+
| ``(`` ... ``)`` | parentheses |
+-------------------+-----------------------------------------------+
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`` |
+------------------------------+--------------------------------------+
.. _Array.length: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length
.. _Array.prototype.entries: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries
.. _Array.prototype.keys: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/keys
.. _Array.prototype.values: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values
.. _Object.entries: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
.. _Object.keys: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
.. _Object.values: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values
.. _String.length: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length
.. _String.prototype.charAt: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt
.. _String.prototype.substring: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring
In the above table, the type aliases (Any
, List
, Optional
and Tuple
) are defined by the
typing
module from the Python Standard Library.
Some of the examples are provided by the test suite while some have been contributed via issues.
.. automodule:: bookstore_test
:members:
:private-members:
:special-members:
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.
The ANTLR v4 grammar for JSONPath is available at jsonpath2/parser/JSONPath.g4
.
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'
Adapted from antlr docs.
antlr4 -Dlanguage=Python3 -o . -lib . jsonpath2/parser/JSONPath.g4