0
# Path Operations
1
2
Core operations for finding, updating, and filtering data using JSONPath expressions. These operations preserve context information and provide full path tracking for located data elements.
3
4
## Capabilities
5
6
### Data Context Management
7
8
JSONPath-NG preserves complete path information and context for all located data elements.
9
10
```python { .api }
11
class DatumInContext:
12
"""Represents a datum along a path from a context."""
13
14
def __init__(self, value, path: JSONPath = None, context: 'DatumInContext' = None):
15
"""
16
Initialize datum with context.
17
18
Args:
19
value: The actual data value
20
path: JSONPath to this datum
21
context: Parent datum context
22
"""
23
24
@classmethod
25
def wrap(cls, data) -> 'DatumInContext':
26
"""Wrap raw data in DatumInContext if not already wrapped"""
27
28
def in_context(self, context, path) -> 'DatumInContext':
29
"""Place this datum within another context"""
30
31
@property
32
def full_path(self) -> JSONPath:
33
"""Complete path from root to this datum"""
34
35
@property
36
def id_pseudopath(self) -> JSONPath:
37
"""Path with IDs substituted when available"""
38
39
value: Any # The actual data value
40
path: JSONPath # JSONPath to this location
41
context: Optional['DatumInContext'] # Parent context
42
```
43
44
Usage example:
45
46
```python
47
from jsonpath_ng import parse
48
49
data = {'users': [{'name': 'Alice', 'id': 1}, {'name': 'Bob', 'id': 2}]}
50
expr = parse('$.users[*].name')
51
52
matches = expr.find(data)
53
for match in matches:
54
print(f"Value: {match.value}")
55
print(f"Full path: {match.full_path}")
56
print(f"Context: {match.context.value}")
57
```
58
59
### Finding Data
60
61
Locate data elements matching JSONPath expressions.
62
63
```python { .api }
64
class JSONPath:
65
def find(self, data) -> Iterable[DatumInContext]:
66
"""
67
Find all data elements matching this JSONPath expression.
68
69
Args:
70
data: JSON data structure to search
71
72
Returns:
73
Iterable of DatumInContext objects containing matching values and their paths
74
"""
75
76
def find_or_create(self, data) -> Iterable[DatumInContext]:
77
"""
78
Find matching elements, creating missing intermediate objects.
79
80
Args:
81
data: JSON data structure to search/modify
82
83
Returns:
84
Iterable of DatumInContext objects, creating empty objects for missing paths
85
"""
86
```
87
88
Usage examples:
89
90
```python
91
from jsonpath_ng import parse
92
93
data = {
94
'store': {
95
'book': [
96
{'title': 'Book 1', 'price': 10.99},
97
{'title': 'Book 2', 'price': 15.99}
98
]
99
}
100
}
101
102
# Find all book titles
103
title_expr = parse('$.store.book[*].title')
104
matches = title_expr.find(data)
105
titles = [match.value for match in matches] # ['Book 1', 'Book 2']
106
107
# Find with creation of missing paths
108
missing_expr = parse('$.store.magazine[*].title')
109
matches = missing_expr.find_or_create(data) # Creates empty magazine array
110
```
111
112
### Updating Data
113
114
Modify data elements at JSONPath locations.
115
116
```python { .api }
117
class JSONPath:
118
def update(self, data, val):
119
"""
120
Update all matching locations with val. Only updates existing paths.
121
122
Args:
123
data: JSON data structure to modify
124
val: New value to set, or callable(old_value, parent_dict, key) -> new_value
125
126
Returns:
127
Modified data structure (same object, modified in place)
128
"""
129
130
def update_or_create(self, data, val):
131
"""
132
Update matching locations with val, creating missing paths.
133
134
Args:
135
data: JSON data structure to modify
136
val: New value to set, or callable(old_value, parent_dict, key) -> new_value
137
138
Returns:
139
Modified data structure (same object, modified in place)
140
"""
141
```
142
143
Usage examples:
144
145
```python
146
from jsonpath_ng import parse
147
148
data = {'users': [{'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 30}]}
149
150
# Update all ages to 35
151
age_expr = parse('$.users[*].age')
152
updated_data = age_expr.update(data, 35)
153
154
# Update with function
155
def increment_age(old_val, parent, key):
156
return old_val + 1
157
158
updated_data = age_expr.update(data, increment_age)
159
160
# Update single user by index
161
single_expr = parse('$.users[0].name')
162
updated_data = single_expr.update(data, 'Alice Smith')
163
164
# Create missing path
165
missing_expr = parse('$.users[*].status')
166
updated_data = missing_expr.update_or_create(data, 'active')
167
```
168
169
### Filtering Data
170
171
Remove data elements based on filter conditions.
172
173
```python { .api }
174
class JSONPath:
175
def filter(self, fn, data):
176
"""
177
Filter matching elements using predicate function.
178
179
Args:
180
fn: Predicate function that returns True for elements to remove
181
data: JSON data structure to filter
182
183
Returns:
184
Filtered data structure with matching elements removed
185
"""
186
```
187
188
Usage examples:
189
190
```python
191
from jsonpath_ng import parse
192
193
data = {
194
'products': [
195
{'name': 'Widget A', 'price': 10.00, 'stock': 5},
196
{'name': 'Widget B', 'price': 15.00, 'stock': 0},
197
{'name': 'Widget C', 'price': 20.00, 'stock': 10}
198
]
199
}
200
201
# Remove out-of-stock products
202
expr = parse('$.products[*]')
203
filtered_data = expr.filter(lambda item: item['stock'] == 0, data)
204
205
# Remove expensive products
206
price_expr = parse('$.products[*].price')
207
filtered_data = price_expr.filter(lambda price: price > 15.00, data)
208
209
# Remove specific fields
210
name_expr = parse('$.products[*].name')
211
filtered_data = name_expr.filter(lambda x: True, data) # Removes all name fields
212
```
213
214
### Automatic ID Generation
215
216
Configure automatic ID field generation for consistent object identification.
217
218
```python { .api }
219
# Module-level configuration
220
import jsonpath_ng.jsonpath as jsonpath
221
222
jsonpath.auto_id_field: Optional[str] = None # Field name for auto-generated IDs
223
224
class AutoIdForDatum(DatumInContext):
225
"""
226
Special DatumInContext that generates automatic IDs based on path.
227
Used when auto_id_field is configured globally.
228
229
The value is always the path leading up to it (not including the ID field),
230
with any ID fields along the way replacing path segments.
231
"""
232
233
def __init__(self, datum, id_field: Optional[str] = None):
234
"""
235
Initialize auto-ID datum.
236
237
Args:
238
datum: Source DatumInContext
239
id_field: Field name for ID (defaults to global auto_id_field)
240
"""
241
242
@property
243
def value(self) -> str:
244
"""Auto-generated ID based on id_pseudopath of the datum"""
245
246
@property
247
def path(self) -> str:
248
"""ID field name"""
249
250
@property
251
def context(self) -> DatumInContext:
252
"""Original datum context"""
253
254
def in_context(self, context, path) -> 'AutoIdForDatum':
255
"""Place this auto-ID datum within another context"""
256
257
def __eq__(self, other) -> bool:
258
"""Compare with another AutoIdForDatum"""
259
```
260
261
Usage example:
262
263
```python
264
import jsonpath_ng.jsonpath as jsonpath
265
from jsonpath_ng import parse
266
267
# Enable auto-ID generation
268
jsonpath.auto_id_field = 'id'
269
270
data = {'users': [{'name': 'Alice'}, {'name': 'Bob'}]}
271
272
# Find auto-generated IDs
273
id_expr = parse('$.users[*].id')
274
matches = id_expr.find(data)
275
ids = [match.value for match in matches] # ['users.0', 'users.1']
276
277
# Mix with existing IDs
278
data = {'users': [{'name': 'Alice', 'id': 'alice'}, {'name': 'Bob'}]}
279
matches = id_expr.find(data)
280
ids = [match.value for match in matches] # ['alice', 'users.1']
281
```
282
283
### Path Utilities
284
285
Helper functions for path manipulation and data structure management.
286
287
```python { .api }
288
def _create_list_key(dict_: dict) -> list:
289
"""
290
Internal helper: Add list to dictionary and return it.
291
Used for list creation in update_or_create operations.
292
293
Args:
294
dict_: Dictionary to add list to
295
296
Returns:
297
Empty list that was added to dictionary
298
"""
299
300
def _clean_list_keys(struct_):
301
"""
302
Internal helper: Replace special list keys with actual lists.
303
Used to clean up data structures after update_or_create operations.
304
305
Args:
306
struct_: Data structure to clean
307
308
Returns:
309
Cleaned data structure with LIST_KEY placeholders replaced by lists
310
"""
311
```
312
313
### Advanced Path Navigation
314
315
Navigate between related data elements using context information.
316
317
```python { .api }
318
class Parent(JSONPath):
319
"""JSONPath that matches the parent node of the current match."""
320
321
def find(self, datum) -> List[DatumInContext]:
322
"""
323
Find parent context of current datum.
324
325
Args:
326
datum: Current DatumInContext
327
328
Returns:
329
List containing parent DatumInContext
330
331
Raises:
332
Error if no parent context exists
333
"""
334
```
335
336
Usage example:
337
338
```python
339
from jsonpath_ng import parse
340
341
data = {
342
'company': {
343
'departments': [
344
{'name': 'Engineering', 'employees': [{'name': 'Alice'}, {'name': 'Bob'}]},
345
{'name': 'Sales', 'employees': [{'name': 'Carol'}]}
346
]
347
}
348
}
349
350
# Find employee names and their department
351
expr = parse('$.company.departments[*].employees[*].name.`parent`.`parent`.name')
352
matches = expr.find(data)
353
dept_names = [match.value for match in matches] # ['Engineering', 'Engineering', 'Sales']
354
```
355
356
## Global Configuration
357
358
```python { .api }
359
# Module-level constants and configuration
360
NOT_SET: object # Sentinel value for missing data
361
LIST_KEY: object # Special key used internally for list operations
362
363
# Configuration variables
364
auto_id_field: Optional[str] = None # Enable automatic ID generation
365
```
366
367
## Error Handling
368
369
Common error patterns and exception handling:
370
371
```python
372
from jsonpath_ng import parse
373
from jsonpath_ng.exceptions import JsonPathParserError
374
375
try:
376
# Invalid JSONPath syntax
377
expr = parse('$.invalid[syntax')
378
except JsonPathParserError as e:
379
print(f"Parse error: {e}")
380
381
# Handle missing data gracefully
382
data = {'users': [{'name': 'Alice'}]}
383
expr = parse('$.users[*].age') # Age field doesn't exist
384
385
matches = expr.find(data) # Returns empty list, no exception
386
values = [match.value for match in matches] # []
387
388
# Safe updates
389
updated = expr.update(data, 25) # No-op, returns original data unchanged
390
```