0
# Extensions
1
2
Extended JSONPath functionality including arithmetic operations, advanced filtering with comparison operators, string manipulation functions, and iterable operations. Extensions are available through the `jsonpath_ng.ext` module.
3
4
## Core Extensions
5
6
### Extended Parser
7
8
Enhanced parser with support for arithmetic, filtering, and additional operators.
9
10
```python { .api }
11
def parse(path: str, debug: bool = False) -> JSONPath:
12
"""
13
Parse JSONPath string with extended syntax support.
14
15
Args:
16
path: JSONPath expression string with extended syntax
17
debug: Enable debug output
18
19
Returns:
20
JSONPath object supporting extended operations
21
"""
22
```
23
24
Usage example:
25
26
```python
27
from jsonpath_ng.ext import parse
28
29
# Arithmetic operations
30
expr = parse('$.products[*].price + $.products[*].tax')
31
32
# Filtering with comparisons
33
expr = parse('$.users[?(@.age > 18)]')
34
35
# String operations
36
expr = parse('$.text.`split(",", *, -1)`')
37
```
38
39
## Arithmetic Operations
40
41
Perform mathematical operations on JSONPath results.
42
43
### Basic Arithmetic
44
45
```python { .api }
46
class Operation(JSONPath):
47
"""Arithmetic operations on JSONPath expressions."""
48
49
def __init__(self, left, op: str, right):
50
"""
51
Initialize arithmetic operation.
52
53
Args:
54
left: Left operand (JSONPath or literal value)
55
op: Operator ('+', '-', '*', '/')
56
right: Right operand (JSONPath or literal value)
57
"""
58
59
def find(self, datum) -> List[DatumInContext]:
60
"""
61
Perform arithmetic operation and return results.
62
63
Returns:
64
List of computed values, empty list if operation fails
65
"""
66
```
67
68
Usage examples:
69
70
```python
71
from jsonpath_ng.ext import parse
72
73
data = {
74
'products': [
75
{'price': 10.00, 'tax': 1.00},
76
{'price': 20.00, 'tax': 2.00}
77
]
78
}
79
80
# Add price and tax
81
expr = parse('$.products[*].price + $.products[*].tax')
82
results = expr.find(data)
83
totals = [r.value for r in results] # [11.00, 22.00]
84
85
# Multiply by constant
86
expr = parse('$.products[*].price * 1.1')
87
results = expr.find(data)
88
marked_up = [r.value for r in results] # [11.00, 22.00]
89
90
# String concatenation
91
data = {'user': {'first': 'John', 'last': 'Doe'}}
92
expr = parse('$.user.first + " " + $.user.last')
93
result = expr.find(data)[0].value # "John Doe"
94
95
# Array operations (same length required)
96
data = {'scores': {'math': [85, 90], 'english': [80, 95]}}
97
expr = parse('$.scores.math[*] + $.scores.english[*]')
98
results = expr.find(data)
99
totals = [r.value for r in results] # [165, 185]
100
```
101
102
### Supported Operators
103
104
```python { .api }
105
# Arithmetic operators
106
OPERATOR_MAP = {
107
'+': operator.add, # Addition/concatenation
108
'-': operator.sub, # Subtraction
109
'*': operator.mul, # Multiplication
110
'/': operator.truediv # Division
111
}
112
```
113
114
## Filtering Operations
115
116
Advanced filtering with comparison operators and boolean logic.
117
118
### Filter Expressions
119
120
```python { .api }
121
class Filter(JSONPath):
122
"""JSONQuery filter for array elements."""
123
124
def __init__(self, expressions):
125
"""
126
Initialize filter.
127
128
Args:
129
expressions: List of filter expression objects
130
"""
131
132
def find(self, datum) -> List[DatumInContext]:
133
"""
134
Apply filter expressions to array elements.
135
136
Returns:
137
List of elements that match all filter expressions
138
"""
139
140
class Expression(JSONPath):
141
"""Filter expression with comparison operator."""
142
143
def __init__(self, left, op: Optional[str], right):
144
"""
145
Initialize filter expression.
146
147
Args:
148
left: Left JSONPath expression
149
op: Comparison operator ('==', '!=', '<', '>', '<=', '>=', '=~')
150
right: Right operand (literal value)
151
"""
152
153
def find(self, datum) -> List[DatumInContext]:
154
"""
155
Evaluate expression against datum.
156
157
Returns:
158
List containing datum if expression is true, empty otherwise
159
"""
160
```
161
162
Usage examples:
163
164
```python
165
from jsonpath_ng.ext import parse
166
167
data = {
168
'employees': [
169
{'name': 'Alice', 'age': 30, 'dept': 'Engineering'},
170
{'name': 'Bob', 'age': 25, 'dept': 'Sales'},
171
{'name': 'Carol', 'age': 35, 'dept': 'Engineering'},
172
{'name': 'Dave', 'age': 28, 'dept': 'Marketing'}
173
]
174
}
175
176
# Filter by age
177
expr = parse('$.employees[?(@.age > 30)]')
178
results = expr.find(data)
179
names = [r.value['name'] for r in results] # ['Carol']
180
181
# Filter by equality
182
expr = parse('$.employees[?(@.dept == "Engineering")]')
183
results = expr.find(data)
184
engineers = [r.value['name'] for r in results] # ['Alice', 'Carol']
185
186
# Filter by inequality
187
expr = parse('$.employees[?(@.age != 25)]')
188
results = expr.find(data)
189
not_25 = [r.value['name'] for r in results] # ['Alice', 'Carol', 'Dave']
190
191
# Regex matching
192
data = {'users': [{'email': 'alice@example.com'}, {'email': 'bob@test.org'}]}
193
expr = parse('$.users[?(@.email =~ ".*@example\\.com")]')
194
results = expr.find(data)
195
example_users = [r.value['email'] for r in results] # ['alice@example.com']
196
197
# Multiple conditions with AND
198
expr = parse('$.employees[?(@.age > 25 & @.dept == "Engineering")]')
199
results = expr.find(data)
200
senior_engineers = [r.value['name'] for r in results] # ['Alice', 'Carol']
201
```
202
203
### Comparison Operators
204
205
```python { .api }
206
# Comparison operators
207
OPERATOR_MAP = {
208
'!=': operator.ne, # Not equal
209
'==': operator.eq, # Equal
210
'=': operator.eq, # Equal (alternative)
211
'<=': operator.le, # Less than or equal
212
'<': operator.lt, # Less than
213
'>=': operator.ge, # Greater than or equal
214
'>': operator.gt, # Greater than
215
'=~': regex_match # Regex match (strings only)
216
}
217
```
218
219
## String Operations
220
221
String manipulation functions for text processing.
222
223
### String Splitting
224
225
```python { .api }
226
class Split(JSONPath):
227
"""String splitting operation."""
228
229
def __init__(self, signature: str):
230
"""
231
Initialize split operation from signature.
232
233
Args:
234
signature: Function signature like "split(sep, segment, maxsplit)"
235
"""
236
237
def find(self, datum) -> List[DatumInContext]:
238
"""
239
Split string value and return specified segment.
240
241
Returns:
242
List containing split result or empty list if operation fails
243
"""
244
```
245
246
Usage examples:
247
248
```python
249
from jsonpath_ng.ext import parse
250
251
data = {'text': 'apple,banana,cherry,date'}
252
253
# Split and get all segments
254
expr = parse('$.text.`split(",", *, -1)`')
255
result = expr.find(data)[0].value # ['apple', 'banana', 'cherry', 'date']
256
257
# Split and get specific segment
258
expr = parse('$.text.`split(",", 1, -1)`')
259
result = expr.find(data)[0].value # 'banana'
260
261
# Split with maxsplit
262
expr = parse('$.text.`split(",", *, 2)`')
263
result = expr.find(data)[0].value # ['apple', 'banana', 'cherry,date']
264
265
# Split on whitespace
266
data = {'sentence': 'The quick brown fox'}
267
expr = parse('$.sentence.`split(" ", 2, -1)`')
268
result = expr.find(data)[0].value # 'brown'
269
```
270
271
### String Substitution
272
273
```python { .api }
274
class Sub(JSONPath):
275
"""String substitution with regex."""
276
277
def __init__(self, signature: str):
278
"""
279
Initialize substitution operation.
280
281
Args:
282
signature: Function signature like "sub(pattern, replacement)"
283
"""
284
285
def find(self, datum) -> List[DatumInContext]:
286
"""
287
Perform regex substitution on string value.
288
289
Returns:
290
List containing substituted string or empty list if operation fails
291
"""
292
```
293
294
Usage examples:
295
296
```python
297
from jsonpath_ng.ext import parse
298
299
data = {'text': 'Hello World 123'}
300
301
# Simple substitution
302
expr = parse('$.text.`sub("World", "Universe")`')
303
result = expr.find(data)[0].value # 'Hello Universe 123'
304
305
# Regex substitution with capture groups
306
expr = parse('$.text.`sub("([a-zA-Z]+) ([a-zA-Z]+) (\\\\d+)", "\\\\3 \\\\2 \\\\1")`')
307
result = expr.find(data)[0].value # '123 World Hello'
308
309
# Remove digits
310
expr = parse('$.text.`sub("\\\\d+", "")`')
311
result = expr.find(data)[0].value # 'Hello World '
312
```
313
314
### String Conversion
315
316
```python { .api }
317
class Str(JSONPath):
318
"""Convert value to string."""
319
320
def __init__(self, signature: str):
321
"""
322
Initialize string conversion.
323
324
Args:
325
signature: Function signature like "str()"
326
"""
327
328
def find(self, datum) -> List[DatumInContext]:
329
"""
330
Convert datum value to string.
331
332
Returns:
333
List containing string representation
334
"""
335
```
336
337
Usage example:
338
339
```python
340
from jsonpath_ng.ext import parse
341
342
data = {'number': 42, 'boolean': True, 'null_val': None}
343
344
# Convert number to string
345
expr = parse('$.number.`str()`')
346
result = expr.find(data)[0].value # '42'
347
348
# Convert boolean to string
349
expr = parse('$.boolean.`str()`')
350
result = expr.find(data)[0].value # 'True'
351
```
352
353
## Iterable Operations
354
355
Operations for working with arrays and objects as collections.
356
357
### Length Operation
358
359
```python { .api }
360
class Len(JSONPath):
361
"""Get length of iterable."""
362
363
def find(self, datum) -> List[DatumInContext]:
364
"""
365
Get length of array, object, or string.
366
367
Returns:
368
List containing length as integer
369
"""
370
```
371
372
Usage examples:
373
374
```python
375
from jsonpath_ng.ext import parse
376
377
data = {
378
'array': [1, 2, 3, 4, 5],
379
'object': {'a': 1, 'b': 2, 'c': 3},
380
'string': 'hello'
381
}
382
383
# Array length
384
expr = parse('$.array.`len`')
385
result = expr.find(data)[0].value # 5
386
387
# Object length (number of keys)
388
expr = parse('$.object.`len`')
389
result = expr.find(data)[0].value # 3
390
391
# String length
392
expr = parse('$.string.`len`')
393
result = expr.find(data)[0].value # 5
394
```
395
396
### Keys Operation
397
398
```python { .api }
399
class Keys(JSONPath):
400
"""Get object keys."""
401
402
def find(self, datum) -> List[DatumInContext]:
403
"""
404
Get keys of object as array.
405
406
Returns:
407
List containing array of object keys
408
"""
409
```
410
411
Usage example:
412
413
```python
414
from jsonpath_ng.ext import parse
415
416
data = {'user': {'name': 'Alice', 'age': 30, 'city': 'New York'}}
417
418
expr = parse('$.user.`keys`')
419
result = expr.find(data)[0].value # ['name', 'age', 'city']
420
```
421
422
### Path Operation
423
424
```python { .api }
425
class Path(JSONPath):
426
"""Get current path as string."""
427
428
def find(self, datum) -> List[DatumInContext]:
429
"""
430
Get JSONPath to current location as string.
431
432
Returns:
433
List containing path string
434
"""
435
```
436
437
Usage example:
438
439
```python
440
from jsonpath_ng.ext import parse
441
442
data = {'users': [{'name': 'Alice'}, {'name': 'Bob'}]}
443
444
expr = parse('$.users[*].name.`path`')
445
results = expr.find(data)
446
paths = [r.value for r in results] # ['$.users[0].name', '$.users[1].name']
447
```
448
449
### Sorting Operation
450
451
```python { .api }
452
class SortedThis(JSONPath):
453
"""Sort array elements."""
454
455
def __init__(self, sorts: Optional[List] = None):
456
"""
457
Initialize sorting operation.
458
459
Args:
460
sorts: List of (path, reverse) tuples for sorting criteria
461
"""
462
463
def find(self, datum) -> List[DatumInContext]:
464
"""
465
Sort array elements by specified criteria.
466
467
Returns:
468
List of sorted elements
469
"""
470
```
471
472
Usage examples:
473
474
```python
475
from jsonpath_ng.ext import parse
476
477
data = {
478
'numbers': [3, 1, 4, 1, 5, 9, 2, 6],
479
'users': [
480
{'name': 'Charlie', 'age': 35},
481
{'name': 'Alice', 'age': 30},
482
{'name': 'Bob', 'age': 25}
483
]
484
}
485
486
# Simple sort
487
expr = parse('$.numbers.`sorted`')
488
result = expr.find(data)[0].value # [1, 1, 2, 3, 4, 5, 6, 9]
489
490
# Sort objects by field (ascending)
491
expr = parse('$.users[\\age]')
492
results = expr.find(data)
493
sorted_users = [r.value for r in results] # Sorted by age ascending
494
495
# Sort by field (descending)
496
expr = parse('$.users[/age]')
497
results = expr.find(data)
498
sorted_users = [r.value for r in results] # Sorted by age descending
499
500
# Sort by multiple fields
501
expr = parse('$.users[\\name, /age]') # Sort by name asc, then age desc
502
results = expr.find(data)
503
sorted_users = [r.value for r in results]
504
```
505
506
## Extended Parser Configuration
507
508
```python { .api }
509
class ExtendedJsonPathLexer(JsonPathLexer):
510
"""Extended lexer with additional tokens for extensions."""
511
512
literals = JsonPathLexer.literals + ['?', '@', '+', '*', '/', '-']
513
tokens = ['BOOL', 'FILTER_OP', 'SORT_DIRECTION', 'FLOAT'] + JsonPathLexer.tokens
514
515
def t_BOOL(self, t) -> Token:
516
"""Parse boolean literals (true/false)"""
517
518
def t_SORT_DIRECTION(self, t) -> Token:
519
"""Parse sort direction indicators (/ for desc, \\ for asc)"""
520
521
def t_FLOAT(self, t) -> Token:
522
"""Parse floating point numbers"""
523
524
class ExtentedJsonPathParser(JsonPathParser):
525
"""Extended parser supporting arithmetic, filtering, and string operations."""
526
527
def __init__(self, debug: bool = False, lexer_class=None):
528
"""
529
Initialize extended parser.
530
531
Args:
532
debug: Enable debug output
533
lexer_class: Custom lexer class (defaults to ExtendedJsonPathLexer)
534
"""
535
```
536
537
## Extension Exceptions
538
539
```python { .api }
540
class DefintionInvalid(Exception):
541
"""Raised when string operation definition syntax is invalid"""
542
```
543
544
## Error Handling
545
546
Extended operations include additional error cases:
547
548
```python
549
from jsonpath_ng.ext import parse
550
551
# Arithmetic type errors
552
data = {'text': 'hello', 'number': 42}
553
expr = parse('$.text + $.number') # String + int
554
results = expr.find(data) # Returns empty list, no exception
555
556
# Division by zero
557
data = {'a': 10, 'b': 0}
558
expr = parse('$.a / $.b')
559
results = expr.find(data) # Returns empty list
560
561
# Invalid regex
562
try:
563
expr = parse('$.text.`sub("[invalid", "replacement")`')
564
results = expr.find({'text': 'hello'})
565
except Exception as e:
566
print(f"Regex error: {e}")
567
568
# Filter on non-array
569
data = {'user': {'name': 'Alice'}}
570
expr = parse('$.user[?(@.name == "Alice")]') # user is not an array
571
results = expr.find(data) # Returns empty list
572
```