|
3 | 3 | The JSONPath2 library has several APIs available to perform JSONPath |
4 | 4 | matching. |
5 | 5 |
|
6 | | -## API |
7 | | - |
8 | | -The API has a high level `match()` method and lower level classes to |
9 | | -interact with the parser directly. |
10 | | - |
11 | | -### `match` shortcut function |
12 | | - |
13 | | -The `jsonpath2.match` function is a shortcut to match a given JSON data |
14 | | -structure against a JSONPath string |
15 | | - |
16 | | -```python |
17 | | ->>> import jsonpath2 |
18 | | ->>> doc = {'hello': 'Hello, world!'} |
19 | | ->>> [x.current_value for x in jsonpath2.match('$.hello', doc)] |
20 | | -['Hello, world!'] |
21 | | -``` |
22 | | - |
23 | | -### `Path` class |
24 | | - |
25 | | -The `jsonpath2.path.Path` class represents a JSONPath. |
26 | | - |
27 | | -```python |
28 | | ->>> s = '{"hello":"Hello, world!"}' |
29 | | -'{"hello":"Hello, world!"}' |
30 | | ->>> import json |
31 | | ->>> d = json.loads(s) |
32 | | -{'hello':'Hello, world!'} |
33 | | ->>> from jsonpath2.path import Path |
34 | | ->>> p = Path.parse_str('$["hello"]') |
35 | | -<jsonpath2.path.Path object> |
36 | | ->>> [match_data.current_value for match_data in p.match(d)] |
37 | | -['Hello, world!'] |
38 | | ->>> [match_data.node.tojsonpath() for match_data in p.match(d)] |
39 | | -['$["hello"]'] |
40 | | -``` |
41 | | - |
42 | | -This class is constructed with respect to the given instance of the `jsonpath2.nodes.root.RootNode` class (viz., the `ro |
43 | | -ot_node` property). |
44 | | - |
45 | | -#### `parse_str(strdata)` class method |
46 | | - |
47 | | -Parse the given string and return a new instance of this class. |
48 | | - |
49 | | -#### `parse_file(fileName, encoding='ascii')` class method |
50 | | - |
51 | | -Parse the contents of the given file and return a new instance of this class. |
52 | | - |
53 | | -#### `match(root_value)` instance method |
54 | | - |
55 | | -Match the given JSON data structure against this instance. |
56 | | -For each match, yield an instance of the `jsonpath2.node.MatchData` class. |
57 | | - |
58 | | -#### `__eq__(other)` instance method |
59 | | - |
60 | | -Tests if two instances are equal. |
61 | | - |
62 | | -#### `__str__()` instance method |
63 | | - |
64 | | -Returns the string representation of this instance. |
65 | | - |
66 | | -#### `root_node` property |
67 | | - |
68 | | -The root node of the abstract syntax tree for this instance. |
69 | | - |
70 | | -### `Node` abstract class |
71 | | - |
72 | | -The `jsonpath2.node.Node` class represents the abstract syntax tree for a JSONPath. |
73 | | - |
74 | | -#### `__eq__(other)` instance method |
75 | | - |
76 | | -Tests if two instances are equal. |
77 | | - |
78 | | -#### `__jsonpath__()` instance method |
79 | | - |
80 | | -Yields the lexer tokens for the string representation of this instance. |
81 | | - |
82 | | -#### `match(root_value, current_value)` instance method |
83 | | - |
84 | | -Match the given root and current JSON data structures against this instance. |
85 | | -For each match, yield an instance of the `jsonpath2.node.MatchData` class. |
86 | | - |
87 | | -#### `tojsonpath()` instance method |
88 | | - |
89 | | -Returns the string representation of this instance. |
90 | | - |
91 | | -### `MatchData` class |
92 | | - |
93 | | -The `jsonpath2.node.MatchData` class represents the JSON value and context for a JSONPath match. |
94 | | - |
95 | | -This class is constructed with respect to a root JSON value, a current JSON value, and an abstract syntax tree node. |
96 | | - |
97 | | -#### `__eq__(other)` instance method |
98 | | - |
99 | | -Tests if two instances are equal. |
100 | | - |
101 | | -#### `root_value` property |
102 | | - |
103 | | -The root JSON value. |
104 | | - |
105 | | -#### `current_value` property |
106 | | - |
107 | | -The current JSON value (i.e., the matching JSON value). |
108 | | - |
109 | | -#### `node` property |
110 | | - |
111 | | -The abstract syntax tree node. |
112 | 6 | ## Syntax |
113 | 7 |
|
114 | 8 | ```eval_rst |
@@ -163,8 +57,9 @@ The abstract syntax tree node. |
163 | 57 |
|
164 | 58 | > See [#14](https://github.com/pacifica/python-jsonpath2/pull/14) for more information. |
165 | 59 |
|
166 | | -The syntax for a function call is the name of the function followed by the arguments in parentheses, i.e., `name(arg1, a |
167 | | -rg2, ..., argN)`, where the arguments are either JSONPaths or JSON values. |
| 60 | +The syntax for a function call is the name of the function followed by the |
| 61 | +arguments in parentheses, i.e., `name(arg1, arg2, ..., argN)`, where the |
| 62 | +arguments are either JSONPaths or JSON values. |
168 | 63 |
|
169 | 64 | ```python |
170 | 65 | >>> s = '{"hello":"Hello, world!"}' |
@@ -300,6 +195,94 @@ rg2, ..., argN)`, where the arguments are either JSONPaths or JSON values. |
300 | 195 | In the above table, the type aliases (`Any`, `List`, `Optional` and `Tuple`) are defined by the |
301 | 196 | [`typing`](https://docs.python.org/3/library/typing.html) module from the Python Standard Library. |
302 | 197 |
|
| 198 | +## Examples |
| 199 | + |
| 200 | +Some of the examples are provided by the test suite while some have been contributed via issues. |
| 201 | + |
| 202 | +### Test Suite Examples |
| 203 | + |
| 204 | +```eval_rst |
| 205 | +.. automodule:: bookstore_test |
| 206 | + :members: |
| 207 | + :private-members: |
| 208 | + :special-members: |
| 209 | +``` |
| 210 | + |
| 211 | +### Issue Examples |
| 212 | + |
| 213 | +#### Issue #19 |
| 214 | + |
| 215 | +This issue involved finding the full path to the matched attribute. |
| 216 | + |
| 217 | +The result isn't strictly supported by the library but code examples are provided. |
| 218 | + |
| 219 | +```python |
| 220 | +import json |
| 221 | +import typing |
| 222 | + |
| 223 | +from jsonpath2.node import Node |
| 224 | +from jsonpath2.nodes.root import RootNode |
| 225 | +from jsonpath2.nodes.subscript import SubscriptNode |
| 226 | +from jsonpath2.nodes.terminal import TerminalNode |
| 227 | +from jsonpath2.path import Path |
| 228 | +from jsonpath2.subscript import Subscript |
| 229 | + |
| 230 | +data = json.loads(""" |
| 231 | +{ |
| 232 | + "values": [ |
| 233 | + {"type": 1, "value": 2}, |
| 234 | + {"type": 2, "value": 3}, |
| 235 | + {"type": 1, "value": 10} |
| 236 | + ] |
| 237 | +} |
| 238 | +""") |
| 239 | + |
| 240 | +path = Path.parse_str("$.values.*[?(@.type = 1)].value") |
| 241 | + |
| 242 | +def get_subscripts(node: Node) -> typing.List[typing.List[Subscript]]: |
| 243 | + return get_subscripts_(node, []) |
| 244 | + |
| 245 | +def get_subscripts_(node: Node, accumulator: typing.List[typing.List[Subscript]]) -> typing.List[typing.List[Subscript]]: |
| 246 | + if isinstance(node, RootNode): |
| 247 | + return get_subscripts_(node.next_node, accumulator) |
| 248 | + elif isinstance(node, SubscriptNode): |
| 249 | + accumulator.append(node.subscripts) |
| 250 | + return get_subscripts_(node.next_node, accumulator) |
| 251 | + elif isinstance(node, TerminalNode): |
| 252 | + return accumulator |
| 253 | + |
| 254 | +for match_data in path.match(data): |
| 255 | + print(f"Value: {match_data.current_value}") |
| 256 | + print(f"JSONPath: {match_data.node.tojsonpath()}") |
| 257 | + print(f"Subscripts: {get_subscripts(match_data.node)}") |
| 258 | + print("") |
| 259 | +``` |
| 260 | + |
| 261 | +The snippet above iterates over the match results, prints the value and |
| 262 | +JSONPath and then prints the list of subscripts. The list of subscripts |
| 263 | +is constructed by traversing the structure of the abstract syntax tree |
| 264 | +for the JSONPath. |
| 265 | + |
| 266 | +The results [modulo the memory addresses] are: |
| 267 | + |
| 268 | +``` |
| 269 | +Value: 2 |
| 270 | +JSONPath: $["values"][0]["value"] |
| 271 | +Subscripts: [[<jsonpath2.subscripts.objectindex.ObjectIndexSubscript object at 0x10f6a3278>], [<jsonpath2.subscripts.arrayindex.ArrayIndexSubscript object at 0x10f6a37b8>], [<jsonpath2.subscripts.objectindex.ObjectIndexSubscript object at 0x10f6a3390>]] |
| 272 | +
|
| 273 | +Value: 10 |
| 274 | +JSONPath: $["values"][2]["value"] |
| 275 | +Subscripts: [[<jsonpath2.subscripts.objectindex.ObjectIndexSubscript object at 0x10f6a3278>], [<jsonpath2.subscripts.arrayindex.ArrayIndexSubscript object at 0x10f6a3978>], [<jsonpath2.subscripts.objectindex.ObjectIndexSubscript object at 0x10f6a3390>]] |
| 276 | +``` |
| 277 | + |
| 278 | +The first subscript is the `"values"` key. The second subscript is the |
| 279 | +index of the `{"type":"value"}` object. The third subscript is the |
| 280 | +`"value"` key. |
| 281 | + |
| 282 | +Note that the result (the list of subscripts) is a list of lists. This |
| 283 | +is because instances of the `SubscriptNode` class are constructed using |
| 284 | +zero or more instances of the `Subscript` class. |
| 285 | + |
303 | 286 | ## Grammar and parser |
304 | 287 |
|
305 | 288 | The [ANTLR v4](https://github.com/antlr/antlr4) grammar for JSONPath is available at `jsonpath2/parser/JSONPath.g4`. |
|
0 commit comments