python-jsonpath
Advanced tools
+4
-1
@@ -88,2 +88,5 @@ # Byte-compiled / optimized / DLL files | ||
| comparison_regression_suite.yaml | ||
| cts.json | ||
| cts.json | ||
| # System | ||
| .DS_Store |
| # SPDX-FileCopyrightText: 2023-present James Prior <jamesgr.prior@gmail.com> | ||
| # | ||
| # SPDX-License-Identifier: MIT | ||
| __version__ = "1.1.1" | ||
| __version__ = "1.2.0" |
@@ -6,2 +6,4 @@ # SPDX-FileCopyrightText: 2023-present James Prior <jamesgr.prior@gmail.com> | ||
| from .env import JSONPathEnvironment | ||
| from .exceptions import JSONPatchError | ||
| from .exceptions import JSONPatchTestFailure | ||
| from .exceptions import JSONPathError | ||
@@ -12,2 +14,3 @@ from .exceptions import JSONPathIndexError | ||
| from .exceptions import JSONPathTypeError | ||
| from .exceptions import JSONPointerEncodeError | ||
| from .exceptions import JSONPointerError | ||
@@ -22,2 +25,3 @@ from .exceptions import JSONPointerIndexError | ||
| from .filter import UNDEFINED | ||
| from .fluent_api import Projection | ||
| from .fluent_api import Query | ||
@@ -42,2 +46,4 @@ from .lex import Lexer | ||
| "JSONPatch", | ||
| "JSONPatchError", | ||
| "JSONPatchTestFailure", | ||
| "JSONPath", | ||
@@ -52,2 +58,3 @@ "JSONPathEnvironment", | ||
| "JSONPointer", | ||
| "JSONPointerEncodeError", | ||
| "JSONPointerError", | ||
@@ -61,2 +68,3 @@ "JSONPointerIndexError", | ||
| "Parser", | ||
| "Projection", | ||
| "query", | ||
@@ -63,0 +71,0 @@ "Query", |
+20
-4
| """Core JSONPath configuration object.""" | ||
| from __future__ import annotations | ||
@@ -320,3 +321,3 @@ | ||
| ) -> Query: | ||
| """Return a `Query` object over matches found by applying _path_ to _data_. | ||
| """Return a `Query` iterator over matches found by applying _path_ to _data_. | ||
@@ -356,4 +357,19 @@ `Query` objects are iterable. | ||
| ``` | ||
| Arguments: | ||
| path: The JSONPath as a string. | ||
| data: A JSON document or Python object implementing the `Sequence` | ||
| or `Mapping` interfaces. | ||
| filter_context: Arbitrary data made available to filters using | ||
| the _filter context_ selector. | ||
| Returns: | ||
| A query iterator. | ||
| Raises: | ||
| JSONPathSyntaxError: If the path is invalid. | ||
| JSONPathTypeError: If a filter expression attempts to use types in | ||
| an incompatible way. | ||
| """ | ||
| return Query(self.finditer(path, data, filter_context=filter_context)) | ||
| return Query(self.finditer(path, data, filter_context=filter_context), self) | ||
@@ -553,5 +569,5 @@ async def findall_async( | ||
| return self._lt(left, right) or self._eq(left, right) | ||
| if operator == "in" and isinstance(right, Sequence): | ||
| if operator == "in" and isinstance(right, (Mapping, Sequence)): | ||
| return left in right | ||
| if operator == "contains" and isinstance(left, Sequence): | ||
| if operator == "contains" and isinstance(left, (Mapping, Sequence)): | ||
| return right in left | ||
@@ -558,0 +574,0 @@ if operator == "=~" and isinstance(right, re.Pattern) and isinstance(left, str): |
@@ -671,2 +671,5 @@ """Filter expression nodes.""" | ||
| def __str__(self) -> str: | ||
| return "#" | ||
| def __eq__(self, other: object) -> bool: | ||
@@ -673,0 +676,0 @@ return isinstance(other, CurrentKey) |
+130
-5
@@ -1,2 +0,3 @@ | ||
| """A fluent API for managing JSONPathMatch iterators.""" | ||
| """A fluent API for working with `JSONPathMatch` iterators.""" | ||
| from __future__ import annotations | ||
@@ -6,9 +7,20 @@ | ||
| import itertools | ||
| from enum import Enum | ||
| from enum import auto | ||
| from typing import TYPE_CHECKING | ||
| from typing import Any | ||
| from typing import Dict | ||
| from typing import Iterable | ||
| from typing import Iterator | ||
| from typing import List | ||
| from typing import Mapping | ||
| from typing import Optional | ||
| from typing import Sequence | ||
| from typing import Tuple | ||
| from typing import Union | ||
| if TYPE_CHECKING: | ||
| from jsonpath import CompoundJSONPath | ||
| from jsonpath import JSONPath | ||
| from jsonpath import JSONPathEnvironment | ||
| from jsonpath import JSONPathMatch | ||
@@ -18,2 +30,17 @@ from jsonpath import JSONPointer | ||
| class Projection(Enum): | ||
| """Projection style used by `Query.select()`.""" | ||
| RELATIVE = auto() | ||
| """The default projection. Selections include parent arrays and objects relative | ||
| to the JSONPathMatch.""" | ||
| ROOT = auto() | ||
| """Selections include parent arrays and objects relative to the root JSON value.""" | ||
| FLAT = auto() | ||
| """All selections are appended to a new array/list, without arrays and objects | ||
| on the path to the selected value.""" | ||
| class Query: | ||
@@ -32,4 +59,5 @@ """A fluent API for managing `JSONPathMatch` iterators. | ||
| def __init__(self, it: Iterable[JSONPathMatch]) -> None: | ||
| def __init__(self, it: Iterable[JSONPathMatch], env: JSONPathEnvironment) -> None: | ||
| self._it = iter(it) | ||
| self._env = env | ||
@@ -124,3 +152,3 @@ def __iter__(self) -> Iterator[JSONPathMatch]: | ||
| def items(self) -> Iterable[Tuple[str, object]]: | ||
| """Return an iterable of (object, path) tuples, one for each match.""" | ||
| """Return an iterable of (path, object) tuples, one for each match.""" | ||
| return ((m.path, m.obj) for m in self._it) | ||
@@ -158,3 +186,3 @@ | ||
| """ | ||
| return tuple(Query(it) for it in itertools.tee(self._it, n)) | ||
| return tuple(Query(it, self._env) for it in itertools.tee(self._it, n)) | ||
@@ -166,2 +194,99 @@ def take(self, n: int) -> Query: | ||
| """ | ||
| return Query(list(itertools.islice(self._it, n))) | ||
| return Query(list(itertools.islice(self._it, n)), self._env) | ||
| def select( | ||
| self, | ||
| *expressions: Union[str, JSONPath, CompoundJSONPath], | ||
| projection: Projection = Projection.RELATIVE, | ||
| ) -> Iterable[object]: | ||
| """Query projection using relative JSONPaths. | ||
| Arguments: | ||
| expressions: One or more JSONPath query expressions to select relative | ||
| to each match in this query iterator. | ||
| projection: The style of projection used when selecting values. Can be | ||
| one of `Projection.RELATIVE`, `Projection.ROOT` or `Projection.FLAT`. | ||
| Defaults to `Projection.RELATIVE`. | ||
| Returns: | ||
| An iterable of objects built from selecting _expressions_ relative to | ||
| each match from the current query. | ||
| **New in version 1.2.0** | ||
| """ | ||
| return filter( | ||
| bool, | ||
| (self._select(m, expressions, projection) for m in self._it), | ||
| ) | ||
| def _select( | ||
| self, | ||
| match: JSONPathMatch, | ||
| expressions: Tuple[Union[str, JSONPath, CompoundJSONPath], ...], | ||
| projection: Projection, | ||
| ) -> object: | ||
| if not isinstance(match.obj, (Mapping, Sequence)) or isinstance(match.obj, str): | ||
| return None | ||
| if projection == Projection.RELATIVE: | ||
| obj: Dict[Union[int, str], Any] = {} | ||
| for expr in expressions: | ||
| path = self._env.compile(expr) if isinstance(expr, str) else expr | ||
| for rel_match in path.finditer(match.obj): # type: ignore | ||
| _patch_obj(rel_match.parts, obj, rel_match.obj) | ||
| return _fix_sparse_arrays(obj) | ||
| if projection == Projection.FLAT: | ||
| arr: List[object] = [] | ||
| for expr in expressions: | ||
| path = self._env.compile(expr) if isinstance(expr, str) else expr | ||
| for rel_match in path.finditer(match.obj): # type: ignore | ||
| arr.append(rel_match.obj) | ||
| return arr | ||
| # Project from the root document | ||
| obj = {} | ||
| for expr in expressions: | ||
| path = self._env.compile(expr) if isinstance(expr, str) else expr | ||
| for rel_match in path.finditer(match.obj): # type: ignore | ||
| _patch_obj(match.parts + rel_match.parts, obj, rel_match.obj) | ||
| return _fix_sparse_arrays(obj) | ||
| def _patch_obj( | ||
| parts: Tuple[Union[int, str], ...], | ||
| obj: Mapping[Union[str, int], Any], | ||
| value: object, | ||
| ) -> None: | ||
| _obj = obj | ||
| # For lack of a better idea, we're patching arrays to dictionaries with | ||
| # integer keys. This is to handle sparse array selections without having | ||
| # to keep track of indexes and how they map from the root JSON value to | ||
| # the selected JSON value. | ||
| # | ||
| # We'll fix these "sparse arrays" after the patch has been applied. | ||
| for part in parts[:-1]: | ||
| if part not in _obj: | ||
| _obj[part] = {} # type: ignore | ||
| _obj = _obj[part] | ||
| _obj[parts[-1]] = value # type: ignore | ||
| def _fix_sparse_arrays(obj: Any) -> object: | ||
| """Fix sparse arrays (dictionaries with integer keys).""" | ||
| if isinstance(obj, str) or not obj: | ||
| return obj | ||
| if isinstance(obj, Sequence): | ||
| return [_fix_sparse_arrays(e) for e in obj] | ||
| if isinstance(obj, Mapping): | ||
| if isinstance(next(iter(obj)), int): | ||
| return [_fix_sparse_arrays(v) for v in obj.values()] | ||
| return {k: _fix_sparse_arrays(v) for k, v in obj.items()} | ||
| return obj |
+3
-2
| """JSONPath tokenization.""" | ||
| from __future__ import annotations | ||
@@ -141,4 +142,4 @@ | ||
| (TOKEN_DOT_PROPERTY, self.dot_property_pattern), | ||
| (TOKEN_FLOAT, r"-?\d+\.\d*(?:e[+-]?\d+)?"), | ||
| (TOKEN_INT, r"-?\d+(?P<G_EXP>e[+\-]?\d+)?\b"), | ||
| (TOKEN_FLOAT, r"-?\d+\.\d*(?:[eE][+-]?\d+)?"), | ||
| (TOKEN_INT, r"-?\d+(?P<G_EXP>[eE][+\-]?\d+)?\b"), | ||
| (TOKEN_DDOT, r"\.\."), | ||
@@ -145,0 +146,0 @@ (TOKEN_AND, self.logical_and_pattern), |
+53
-1
@@ -33,2 +33,4 @@ """The default JSONPath parser.""" | ||
| from .filter import ListLiteral | ||
| from .filter import Literal | ||
| from .filter import Nil | ||
| from .filter import Path | ||
@@ -195,5 +197,22 @@ from .filter import PrefixExpression | ||
| "!=", | ||
| "=~", | ||
| ] | ||
| ) | ||
| # Infix operators that accept filter expression literals. | ||
| INFIX_LITERAL_OPERATORS = frozenset( | ||
| [ | ||
| "==", | ||
| ">=", | ||
| ">", | ||
| "<=", | ||
| "<", | ||
| "!=", | ||
| "<>", | ||
| "=~", | ||
| "in", | ||
| "contains", | ||
| ] | ||
| ) | ||
| PREFIX_OPERATORS = frozenset( | ||
@@ -460,2 +479,8 @@ [ | ||
| if stream.peek.kind == TOKEN_RBRACKET: | ||
| raise JSONPathSyntaxError( | ||
| "unexpected trailing comma", | ||
| token=stream.peek, | ||
| ) | ||
| stream.next_token() | ||
@@ -483,2 +508,9 @@ | ||
| if isinstance(expr, (Literal, Nil)): | ||
| raise JSONPathSyntaxError( | ||
| "filter expression literals outside of " | ||
| "function expressions must be compared", | ||
| token=tok, | ||
| ) | ||
| return Filter(env=self.env, token=tok, expression=BooleanExpression(expr)) | ||
@@ -527,2 +559,16 @@ | ||
| if operator not in self.INFIX_LITERAL_OPERATORS: | ||
| if isinstance(left, (Literal, Nil)): | ||
| raise JSONPathSyntaxError( | ||
| "filter expression literals outside of " | ||
| "function expressions must be compared", | ||
| token=tok, | ||
| ) | ||
| if isinstance(right, (Literal, Nil)): | ||
| raise JSONPathSyntaxError( | ||
| "filter expression literals outside of " | ||
| "function expressions must be compared", | ||
| token=tok, | ||
| ) | ||
| return InfixExpression(left, operator, right) | ||
@@ -540,2 +586,9 @@ | ||
| ) | ||
| if stream.current.kind not in self.BINARY_OPERATORS: | ||
| raise JSONPathSyntaxError( | ||
| f"expected an expression, found '{stream.current.value}'", | ||
| token=stream.current, | ||
| ) | ||
| expr = self.parse_infix_expression(stream, expr) | ||
@@ -548,3 +601,2 @@ | ||
| root = stream.next_token() | ||
| assert root.kind in {TOKEN_ROOT, TOKEN_FAKE_ROOT} # XXX: | ||
| return RootPath( | ||
@@ -551,0 +603,0 @@ JSONPath( |
+116
-0
| """JSON Patch, as per RFC 6902.""" | ||
| from __future__ import annotations | ||
@@ -88,2 +89,74 @@ | ||
| class OpAddNe(OpAdd): | ||
| """A non-standard _add if not exists_ operation. | ||
| This is like _OpAdd_, but only adds object/dict keys/values if they key does | ||
| not already exist. | ||
| **New in version 1.2.0** | ||
| """ | ||
| __slots__ = ("path", "value") | ||
| name = "addne" | ||
| def apply( | ||
| self, data: Union[MutableSequence[object], MutableMapping[str, object]] | ||
| ) -> Union[MutableSequence[object], MutableMapping[str, object]]: | ||
| """Apply this patch operation to _data_.""" | ||
| parent, obj = self.path.resolve_parent(data) | ||
| if parent is None: | ||
| # Replace the root object. | ||
| # The following op, if any, will raise a JSONPatchError if needed. | ||
| return self.value # type: ignore | ||
| target = self.path.parts[-1] | ||
| if isinstance(parent, MutableSequence): | ||
| if obj is UNDEFINED: | ||
| parent.append(self.value) | ||
| else: | ||
| parent.insert(int(target), self.value) | ||
| elif isinstance(parent, MutableMapping) and target not in parent: | ||
| parent[target] = self.value | ||
| return data | ||
| class OpAddAp(OpAdd): | ||
| """A non-standard add operation that appends to arrays/lists . | ||
| This is like _OpAdd_, but assumes an index of "-" if the path can not | ||
| be resolved. | ||
| **New in version 1.2.0** | ||
| """ | ||
| __slots__ = ("path", "value") | ||
| name = "addap" | ||
| def apply( | ||
| self, data: Union[MutableSequence[object], MutableMapping[str, object]] | ||
| ) -> Union[MutableSequence[object], MutableMapping[str, object]]: | ||
| """Apply this patch operation to _data_.""" | ||
| parent, obj = self.path.resolve_parent(data) | ||
| if parent is None: | ||
| # Replace the root object. | ||
| # The following op, if any, will raise a JSONPatchError if needed. | ||
| return self.value # type: ignore | ||
| target = self.path.parts[-1] | ||
| if isinstance(parent, MutableSequence): | ||
| if obj is UNDEFINED: | ||
| parent.append(self.value) | ||
| else: | ||
| parent.insert(int(target), self.value) | ||
| elif isinstance(parent, MutableMapping): | ||
| parent[target] = self.value | ||
| else: | ||
| raise JSONPatchError( | ||
| f"unexpected operation on {parent.__class__.__name__!r}" | ||
| ) | ||
| return data | ||
| class OpRemove(Op): | ||
@@ -344,2 +417,12 @@ """The JSON Patch _remove_ operation.""" | ||
| ) | ||
| elif op == "addne": | ||
| self.addne( | ||
| path=self._op_pointer(operation, "path", "addne", i), | ||
| value=self._op_value(operation, "value", "addne", i), | ||
| ) | ||
| elif op == "addap": | ||
| self.addne( | ||
| path=self._op_pointer(operation, "path", "addap", i), | ||
| value=self._op_value(operation, "value", "addap", i), | ||
| ) | ||
| elif op == "remove": | ||
@@ -429,2 +512,34 @@ self.remove(path=self._op_pointer(operation, "path", "add", i)) | ||
| def addne(self: Self, path: Union[str, JSONPointer], value: object) -> Self: | ||
| """Append an _addne_ operation to this patch. | ||
| Arguments: | ||
| path: A string representation of a JSON Pointer, or one that has | ||
| already been parsed. | ||
| value: The object to add. | ||
| Returns: | ||
| This `JSONPatch` instance, so we can build a JSON Patch by chaining | ||
| calls to JSON Patch operation methods. | ||
| """ | ||
| pointer = self._ensure_pointer(path) | ||
| self.ops.append(OpAddNe(path=pointer, value=value)) | ||
| return self | ||
| def addap(self: Self, path: Union[str, JSONPointer], value: object) -> Self: | ||
| """Append an _addap_ operation to this patch. | ||
| Arguments: | ||
| path: A string representation of a JSON Pointer, or one that has | ||
| already been parsed. | ||
| value: The object to add. | ||
| Returns: | ||
| This `JSONPatch` instance, so we can build a JSON Patch by chaining | ||
| calls to JSON Patch operation methods. | ||
| """ | ||
| pointer = self._ensure_pointer(path) | ||
| self.ops.append(OpAddAp(path=pointer, value=value)) | ||
| return self | ||
| def remove(self: Self, path: Union[str, JSONPointer]) -> Self: | ||
@@ -557,2 +672,3 @@ """Append a _remove_ operation to this patch. | ||
| raise JSONPatchError(f"{err} ({op.name}:{i})") from err | ||
| return _data | ||
@@ -559,0 +675,0 @@ |
+49
-0
@@ -18,2 +18,3 @@ # noqa: D100 | ||
| from jsonpath._data import load_data | ||
| from jsonpath.fluent_api import Query | ||
| from jsonpath.match import FilterContextVars | ||
@@ -214,2 +215,26 @@ from jsonpath.match import JSONPathMatch | ||
| def query( | ||
| self, | ||
| data: Union[str, IOBase, Sequence[Any], Mapping[str, Any]], | ||
| *, | ||
| filter_context: Optional[FilterContextVars] = None, | ||
| ) -> Query: | ||
| """Return a `Query` iterator over matches found by applying this path to _data_. | ||
| Arguments: | ||
| data: A JSON document or Python object implementing the `Sequence` | ||
| or `Mapping` interfaces. | ||
| filter_context: Arbitrary data made available to filters using | ||
| the _filter context_ selector. | ||
| Returns: | ||
| A query iterator. | ||
| Raises: | ||
| JSONPathSyntaxError: If the path is invalid. | ||
| JSONPathTypeError: If a filter expression attempts to use types in | ||
| an incompatible way. | ||
| """ | ||
| return Query(self.finditer(data, filter_context=filter_context), self.env) | ||
| def empty(self) -> bool: | ||
@@ -412,2 +437,26 @@ """Return `True` if this path has no selectors.""" | ||
| def query( | ||
| self, | ||
| data: Union[str, IOBase, Sequence[Any], Mapping[str, Any]], | ||
| *, | ||
| filter_context: Optional[FilterContextVars] = None, | ||
| ) -> Query: | ||
| """Return a `Query` iterator over matches found by applying this path to _data_. | ||
| Arguments: | ||
| data: A JSON document or Python object implementing the `Sequence` | ||
| or `Mapping` interfaces. | ||
| filter_context: Arbitrary data made available to filters using | ||
| the _filter context_ selector. | ||
| Returns: | ||
| A query iterator. | ||
| Raises: | ||
| JSONPathSyntaxError: If the path is invalid. | ||
| JSONPathTypeError: If a filter expression attempts to use types in | ||
| an incompatible way. | ||
| """ | ||
| return Query(self.finditer(data, filter_context=filter_context), self.env) | ||
| def union(self, path: JSONPath) -> CompoundJSONPath: | ||
@@ -414,0 +463,0 @@ """Union of this path and another path.""" |
| """JSON Pointer. See https://datatracker.ietf.org/doc/html/rfc6901.""" | ||
| from __future__ import annotations | ||
@@ -329,2 +330,5 @@ | ||
| def __hash__(self) -> int: | ||
| return hash(self.parts) | ||
| def __repr__(self) -> str: | ||
@@ -331,0 +335,0 @@ return f"JSONPointer({self._s!r})" |
+12
-3
| Metadata-Version: 2.1 | ||
| Name: python-jsonpath | ||
| Version: 1.1.1 | ||
| Version: 1.2.0 | ||
| Summary: JSONPath, JSON Pointer and JSON Patch for Python. | ||
@@ -59,2 +59,3 @@ Project-URL: Documentation, https://jg-rp.github.io/python-jsonpath/ | ||
| - [Links](#links) | ||
| - [Related projects](#related-projects) | ||
| - [Examples](#examples) | ||
@@ -92,2 +93,10 @@ - [License](#license) | ||
| ## Related projects | ||
| - [Python JSONPath RFC 9535](https://github.com/jg-rp/python-jsonpath-rfc9535) - An implementation of JSONPath that follows RFC 9535 much more strictly. If you require maximum interoperability with JSONPath implemented in other languages - at the expense of extra features - choose python-jsonpath-rfc9535 over python-jsonpath. | ||
| python-jsonpath-rfc9535 matches RFC 9535's JSONPath model internally and is careful to match the spec's terminology. It also includes utilities for verifying and testing the [JSONPath Compliance Test Suite](https://github.com/jsonpath-standard/jsonpath-compliance-test-suite). Most notably the nondeterministic behavior of some JSONPath selectors. | ||
| - [JSON P3](https://github.com/jg-rp/json-p3) - RFC 9535 implemented in TypeScript. JSON P3 does not include all the non-standard features of Python JSONPath, but does define some optional [extra syntax](https://jg-rp.github.io/json-p3/guides/jsonpath-extra). | ||
| ## Examples | ||
@@ -115,3 +124,3 @@ | ||
| Since version 0.8.0, we include an [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901) compliant implementation of JSON Pointer. See JSON Pointer [quick start](https://jg-rp.github.io/python-jsonpath/quickstart/#pointerresolvepointer-data), [guide](https://jg-rp.github.io/python-jsonpath/pointers/) and [API reference](https://jg-rp.github.io/python-jsonpath/api/#jsonpath.JSONPointer) | ||
| We include an [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901) compliant implementation of JSON Pointer. See JSON Pointer [quick start](https://jg-rp.github.io/python-jsonpath/quickstart/#pointerresolvepointer-data), [guide](https://jg-rp.github.io/python-jsonpath/pointers/) and [API reference](https://jg-rp.github.io/python-jsonpath/api/#jsonpath.JSONPointer) | ||
@@ -139,3 +148,3 @@ ```python | ||
| Since version 0.8.0, we also include an [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) compliant implementation of JSON Patch. See JSON Patch [quick start](https://jg-rp.github.io/python-jsonpath/quickstart/#patchapplypatch-data) and [API reference](https://jg-rp.github.io/python-jsonpath/api/#jsonpath.JSONPatch) | ||
| We also include an [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) compliant implementation of JSON Patch. See JSON Patch [quick start](https://jg-rp.github.io/python-jsonpath/quickstart/#patchapplypatch-data) and [API reference](https://jg-rp.github.io/python-jsonpath/api/#jsonpath.JSONPatch) | ||
@@ -142,0 +151,0 @@ ```python |
+11
-2
@@ -34,2 +34,3 @@ <h1 align="center">Python JSONPath</h1> | ||
| - [Links](#links) | ||
| - [Related projects](#related-projects) | ||
| - [Examples](#examples) | ||
@@ -67,2 +68,10 @@ - [License](#license) | ||
| ## Related projects | ||
| - [Python JSONPath RFC 9535](https://github.com/jg-rp/python-jsonpath-rfc9535) - An implementation of JSONPath that follows RFC 9535 much more strictly. If you require maximum interoperability with JSONPath implemented in other languages - at the expense of extra features - choose python-jsonpath-rfc9535 over python-jsonpath. | ||
| python-jsonpath-rfc9535 matches RFC 9535's JSONPath model internally and is careful to match the spec's terminology. It also includes utilities for verifying and testing the [JSONPath Compliance Test Suite](https://github.com/jsonpath-standard/jsonpath-compliance-test-suite). Most notably the nondeterministic behavior of some JSONPath selectors. | ||
| - [JSON P3](https://github.com/jg-rp/json-p3) - RFC 9535 implemented in TypeScript. JSON P3 does not include all the non-standard features of Python JSONPath, but does define some optional [extra syntax](https://jg-rp.github.io/json-p3/guides/jsonpath-extra). | ||
| ## Examples | ||
@@ -90,3 +99,3 @@ | ||
| Since version 0.8.0, we include an [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901) compliant implementation of JSON Pointer. See JSON Pointer [quick start](https://jg-rp.github.io/python-jsonpath/quickstart/#pointerresolvepointer-data), [guide](https://jg-rp.github.io/python-jsonpath/pointers/) and [API reference](https://jg-rp.github.io/python-jsonpath/api/#jsonpath.JSONPointer) | ||
| We include an [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901) compliant implementation of JSON Pointer. See JSON Pointer [quick start](https://jg-rp.github.io/python-jsonpath/quickstart/#pointerresolvepointer-data), [guide](https://jg-rp.github.io/python-jsonpath/pointers/) and [API reference](https://jg-rp.github.io/python-jsonpath/api/#jsonpath.JSONPointer) | ||
@@ -114,3 +123,3 @@ ```python | ||
| Since version 0.8.0, we also include an [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) compliant implementation of JSON Patch. See JSON Patch [quick start](https://jg-rp.github.io/python-jsonpath/quickstart/#patchapplypatch-data) and [API reference](https://jg-rp.github.io/python-jsonpath/api/#jsonpath.JSONPatch) | ||
| We also include an [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) compliant implementation of JSON Patch. See JSON Patch [quick start](https://jg-rp.github.io/python-jsonpath/quickstart/#patchapplypatch-data) and [API reference](https://jg-rp.github.io/python-jsonpath/api/#jsonpath.JSONPatch) | ||
@@ -117,0 +126,0 @@ ```python |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
235359
6.61%5400
5.94%