A final implementation of JSONPath for Python that aims to be standard compliant, including arithmetic and binary comparison operators and providing clear AST for metaprogramming.
—
Parse JSONPath expressions from strings or build them programmatically using AST classes. JSONPath-NG provides a full parser for standard JSONPath syntax and supports programmatic construction for complex use cases.
Parse JSONPath expressions from strings into executable JSONPath objects.
def parse(string: str) -> JSONPath:
"""
Parse a JSONPath string expression into a JSONPath object.
Args:
string: JSONPath expression string (e.g., '$.foo[*].bar')
Returns:
JSONPath object that can be used for find/update/filter operations
Raises:
JsonPathParserError: If the string contains invalid JSONPath syntax
"""Usage example:
from jsonpath_ng import parse
# Simple field access
expr = parse('$.users[*].name')
# Complex nested paths
expr = parse('$.store.book[0].author')
# Wildcards and slicing
expr = parse('$.products[*].reviews[1:3]')
# Union expressions
expr = parse('$.users[*].(name|email)')Reference to the root object in JSON data structure.
class Root(JSONPath):
"""JSONPath referring to the root object. Concrete syntax is '$'."""
def find(self, data) -> List[DatumInContext]:
"""Find root object, returns single-element list containing root data"""
def update(self, data, val):
"""Replace entire root object with val"""
def filter(self, fn, data):
"""Apply filter function to root data"""Reference to the current object in JSONPath context.
class This(JSONPath):
"""JSONPath referring to current datum. Concrete syntax is '@' or '`this`'."""
def find(self, datum) -> List[DatumInContext]:
"""Find current datum, returns single-element list"""
def update(self, data, val):
"""Replace current data with val"""
def filter(self, fn, data):
"""Apply filter function to current data"""Access object fields by name, including wildcards and multi-field selection.
class Fields(JSONPath):
"""JSONPath referring to object fields. Supports wildcards and multiple fields."""
def __init__(self, *fields: str):
"""
Initialize field accessor.
Args:
*fields: Field names to access. Use '*' for all fields.
"""
def find(self, datum) -> List[DatumInContext]:
"""Find values for specified fields"""
def find_or_create(self, datum) -> List[DatumInContext]:
"""Find values, creating empty objects for missing fields"""
def update(self, data, val):
"""Update specified fields with val"""
def update_or_create(self, data, val):
"""Update fields, creating them if they don't exist"""
def filter(self, fn, data):
"""Remove fields where filter function returns True"""
def reified_fields(self, datum) -> Tuple[str, ...]:
"""Get actual field names, expanding wildcards"""
@staticmethod
def get_field_datum(datum, field: str, create: bool) -> Optional[DatumInContext]:
"""Get datum for specific field, optionally creating if missing"""Access array elements by numeric index.
class Index(JSONPath):
"""JSONPath for array indices. Concrete syntax is '[n]'."""
def __init__(self, index: int):
"""
Initialize index accessor.
Args:
index: Array index (0-based, negative indices supported)
"""
def find(self, datum) -> List[DatumInContext]:
"""Find element at specified index"""
def find_or_create(self, datum) -> List[DatumInContext]:
"""Find element, padding array if index is beyond current length"""
def update(self, data, val):
"""Update element at specified index"""
def update_or_create(self, data, val):
"""Update element, expanding array if necessary"""
def filter(self, fn, data):
"""Remove element if filter function returns True"""Access array slices using Python-style slice notation.
class Slice(JSONPath):
"""JSONPath for array slices. Supports '[start:end:step]' and '[*]' syntax."""
def __init__(self, start: Optional[int] = None, end: Optional[int] = None, step: Optional[int] = None):
"""
Initialize slice accessor.
Args:
start: Start index (inclusive)
end: End index (exclusive)
step: Step size
"""
def find(self, datum) -> List[DatumInContext]:
"""Find elements in slice range"""
def update(self, data, val):
"""Update all elements in slice range"""
def filter(self, fn, data):
"""Remove elements in slice where filter function returns True"""Combine JSONPath expressions using composition operators.
class Child(JSONPath):
"""JSONPath combining left and right expressions. Concrete syntax is 'left.right'."""
def __init__(self, left: JSONPath, right: JSONPath):
"""
Initialize child expression.
Args:
left: Parent JSONPath expression
right: Child JSONPath expression
"""
def find(self, datum) -> List[DatumInContext]:
"""Find right expression results starting from left expression matches"""
def find_or_create(self, datum) -> List[DatumInContext]:
"""Find with creation of missing intermediate objects"""
def update(self, data, val):
"""Update right expression targets within left expression matches"""
def update_or_create(self, data, val):
"""Update with creation of missing intermediate objects"""
def filter(self, fn, data):
"""Apply filter to right expression targets within left expression matches"""
class Union(JSONPath):
"""JSONPath union of results. Concrete syntax is 'left|right'."""
def __init__(self, left: JSONPath, right: JSONPath):
"""
Initialize union expression.
Args:
left: First JSONPath expression
right: Second JSONPath expression
"""
def find(self, data) -> List[DatumInContext]:
"""Find union of left and right expression results"""
def is_singular(self) -> bool:
"""Always returns False as unions produce multiple results"""
class Descendants(JSONPath):
"""JSONPath recursive descendant matching. Concrete syntax is 'left..right'."""
def __init__(self, left: JSONPath, right: JSONPath):
"""
Initialize descendants expression.
Args:
left: Ancestor JSONPath expression
right: Descendant JSONPath expression to match
"""
def find(self, datum) -> List[DatumInContext]:
"""Find right expression matches recursively within left expression results"""
def update(self, data, val):
"""Update all descendant matches"""
def filter(self, fn, data):
"""Filter all descendant matches"""
def is_singular(self) -> bool:
"""Always returns False as descendants produce multiple results"""Filter expressions based on conditions.
class Where(JSONPath):
"""JSONPath filtering based on condition. Concrete syntax is 'left where right'."""
def __init__(self, left: JSONPath, right: JSONPath):
"""
Initialize where expression.
Args:
left: Base JSONPath expression
right: Condition JSONPath expression
"""
def find(self, data) -> List[DatumInContext]:
"""Find left expression results that have matches for right expression"""
def update(self, data, val):
"""Update matching left expression results"""
def filter(self, fn, data):
"""Apply filter to matching left expression results"""
class WhereNot(Where):
"""JSONPath filtering based on negated condition. Concrete syntax is 'left wherenot right'."""
def find(self, data) -> List[DatumInContext]:
"""Find left expression results that do NOT have matches for right expression"""Additional expression types for advanced use cases.
class Parent(JSONPath):
"""JSONPath matching parent node. Available via named operator '`parent`'."""
def find(self, datum) -> List[DatumInContext]:
"""Find parent of current datum"""
class Intersect(JSONPath):
"""JSONPath intersection (not implemented). Concrete syntax is 'left&right'."""
def __init__(self, left: JSONPath, right: JSONPath):
"""Initialize intersection expression (not implemented)"""
def find(self, data) -> List[DatumInContext]:
"""Not implemented - raises NotImplementedError"""
def is_singular(self) -> bool:
"""Always returns False"""Build JSONPath expressions programmatically without parsing strings:
from jsonpath_ng.jsonpath import Root, Fields, Index, Child, Slice
# Equivalent to '$.foo[*].bar'
expr = Root().child(Fields('foo')).child(Slice()).child(Fields('bar'))
# Equivalent to '$.users[0].profile.name'
expr = Child(
Child(
Child(Root(), Fields('users')),
Index(0)
),
Child(Fields('profile'), Fields('name'))
)
# Using the convenience child() method
expr = Root().child(Fields('users')).child(Index(0)).child(Fields('profile')).child(Fields('name'))class JsonPathParser:
"""LALR parser for JSONPath expressions."""
def __init__(self, debug: bool = False, lexer_class=None):
"""
Initialize parser.
Args:
debug: Enable debug output
lexer_class: Custom lexer class (defaults to JsonPathLexer)
"""
def parse(self, string: str, lexer=None) -> JSONPath:
"""Parse JSONPath string into JSONPath object"""
def parse_token_stream(self, token_iterator) -> JSONPath:
"""Parse from token iterator"""
class JsonPathLexer:
"""Lexical analyzer for JSONPath expressions."""
def __init__(self, debug: bool = False):
"""
Initialize lexer.
Args:
debug: Enable debug output
"""
def tokenize(self, string: str) -> Iterator[Token]:
"""
Tokenize JSONPath string.
Args:
string: JSONPath expression string
Returns:
Iterator of tokens for parsing
"""
# Class attributes
literals: List[str] # List of literal characters
reserved_words: Dict[str, str] # Reserved word mappings
tokens: List[str] # List of token typesclass JSONPathError(Exception):
"""Base exception for JSONPath operations"""
class JsonPathParserError(JSONPathError):
"""Parser-specific exceptions for invalid JSONPath syntax"""
class JsonPathLexerError(JSONPathError):
"""Lexer-specific exceptions for invalid tokens"""Install with Tessl CLI
npx tessl i tessl/pypi-jsonpath-ng