Python bindings for fast JSONPath resolve and value-to-path search implemented in Rust
Project description
JSONPath Sleuth
Fast Python bindings (via Rust + PyO3) for:
- Resolving JSONPath expressions against Python dict/list JSON
- Finding JSONPath-like paths for all occurrences of a target value
- Extracting all JSONPath-like paths paired with their leaf values
Install / Build
- Option A: editable dev install
- pip install maturin
- maturin develop -m pyproject.toml
- Option B: build wheel
- maturin build -m pyproject.toml
- pip install dist/*.whl
Requires Python 3.8+.
Run Tests
-
Rust unit tests (no Python needed)
cargo test --no-default-features- Also compile PyO3 bindings:
PYO3_PYTHON=$(which python3) cargo test --features python
-
Python tests (pytest)
- Option A (quick):
python3 -m venv .venv && source .venv/bin/activatepip install -U pip pytest maturinmaturin develop -m Cargo.toml --features pythonpytest -q
- Option B (dev extra):
python3 -m venv .venv && source .venv/bin/activatepip install -U pippip install -e .[dev]maturin develop -m Cargo.toml --features pythonpytest -q
- Option A (quick):
Notes
- Re-run
maturin developafter Rust changes to refresh the extension in your venv. - If pytest cannot import
jsonpath_sleuth, ensure you activated the same venv used formaturin develop.
Publish
-
Build wheels + sdist
maturin build -m Cargo.toml --features python --release --sdist
-
TestPyPI (requires separate TestPyPI account and token)
- Publish:
maturin publish -m Cargo.toml --features python --repository-url https://test.pypi.org/legacy/ -u __token__ -p <pypi-TEST_TOKEN> - Install to verify:
pip install -i https://test.pypi.org/simple jsonpath-sleuth
- Publish:
-
PyPI
- Publish:
maturin publish -m Cargo.toml --features python -u __token__ -p <pypi-PROD_TOKEN> - Install to verify:
pip install jsonpath-sleuth
- Publish:
Tips
- Bump version in both
Cargo.tomlandpyproject.tomlbefore publishing a new release. - Tokens begin with
pypi-. Avoid committing tokens; pass on the command line or configure~/.pypirc.
Python API
Module: jsonpath_sleuth
resolve_jsonpath(data: dict | list, path: str) -> list[Any]- Returns a list of matched values for the given JSONPath. The path may omit the leading
$(it is added automatically).
- Returns a list of matched values for the given JSONPath. The path may omit the leading
find_jsonpaths_by_value(data: dict | list, target: Any) -> list[str]- Returns string paths like
foo.bar[0].bazwhere value equalstarget.
- Returns string paths like
extract_jsonpaths_and_values(data: dict | list) -> list[tuple[str, Any]]- Returns all JSONPath-like paths paired with their leaf values. Paths use
.for object keys and[idx]for arrays.
- Returns all JSONPath-like paths paired with their leaf values. Paths use
Examples
from jsonpath_sleuth import resolve_jsonpath, find_jsonpaths_by_value, extract_jsonpaths_and_values
obj = {
"store": {
"book": [
{"category": "fiction", "title": "Sword"},
{"category": "fiction", "title": "Shield"},
],
"bicycle": {"color": "red", "price": 19.95},
}
}
# 1) Resolve JSONPath (prefix not required)
print(resolve_jsonpath(obj, "store.book[*].title"))
# -> ["Sword", "Shield"]
# Also works with explicit JSONPath:
print(resolve_jsonpath(obj, "$.store.book[*].title"))
# -> ["Sword", "Shield"]
# 2) Find paths by target value
print(find_jsonpaths_by_value(obj, "fiction"))
# -> ["store.book[0].category", "store.book[1].category"]
# 3) Extract all leaf paths and values
print(extract_jsonpaths_and_values(obj))
# -> [
# ("store.book[0].category", "fiction"),
# ("store.book[0].title", "Sword"),
# ("store.book[1].category", "fiction"),
# ("store.book[1].title", "Shield"),
# ("store.bicycle.color", "red"),
# ("store.bicycle.price", 19.95),
# ]
Advanced: Nested Wildcard Filters
JSONPath Sleuth supports nested wildcard filters - a powerful feature that most JSONPath libraries don't handle well:
from jsonpath_sleuth import resolve_jsonpath
data = {
"parties": [
{
"name": "Alice",
"results": [
{"item": "A"},
{"item": "B"}
]
},
{
"name": "Bob",
"results": []
},
{
"name": "Charlie",
"results": [
{"item": "A"},
{"item": "C"}
]
}
]
}
# Find all parties that have ANY result with item='A'
print(resolve_jsonpath(data, "parties[?(@.results[*].item=='A')].name"))
# -> ["Alice", "Charlie"]
# This pattern works: <base>[?(@.<nested_array>[*].<field>=='<value>')].<result_field>
How it works:
- Checks if ANY item in the nested array matches the condition
- Returns the specified field from matching parent objects
- Custom implementation handles what standard JSONPath libraries can't
Apostrophes in Filter Values
Filter expressions support apostrophes in comparison strings by escaping them with backslashes:
from jsonpath_sleuth import resolve_jsonpath
# Simple filter with apostrophe
data = [
{"name": "item with's", "amount": 100},
{"name": "plain item", "amount": 200},
{"name": "item with's", "amount": 300},
]
result = resolve_jsonpath(data, r"[?(@.name == 'item with\'s')].amount")
# -> [100, 300]
# Nested wildcard with apostrophe
nested_data = {
"items": [
{"name": "item1", "results": [{"type": "value's type"}, {"type": "other"}]},
{"name": "item2", "results": [{"type": "value's type"}]},
]
}
result = resolve_jsonpath(
nested_data,
r"items[?(@.results[*].type=='value\'s type')].name"
)
# -> ["item1", "item2"]
# Multiple apostrophes in a single filter
multi_data = [
{"desc": "Mary's and John's", "id": "a"},
{"desc": "other", "id": "b"},
]
result = resolve_jsonpath(multi_data, r"[?(@.desc == 'Mary\'s and John\'s')].id")
# -> ["a"]
When writing JSONPath queries in Python:
- Use raw strings (
r"...") to avoid double-escaping:r"[?(@.name == 'user\'s name')]" - Escape apostrophes with backslash:
\' - Works with both simple and nested wildcard filters
Notes
JSONPath Support
- Standard JSONPath is powered by
jsonpath-rustcrate - Nested wildcard filters use custom implementation for enhanced functionality
- JSONPath keys with spaces or special characters must be quoted using bracket notation
- Example: use
a['some key'].nextinstead ofa.some key.next - You may omit the leading
$; the resolver adds it automatically
- Example: use
- Filter values with apostrophes must escape them:
'user\'s name'
Path Format
- Paths produced by value search use
.between object keys and[idx]for arrays - If the entire input equals the target, no paths are returned (empty list)
Supported Patterns
- ✅ Standard wildcards:
store.book[*].title - ✅ Filters:
store.book[?(@.price < 10)].title - ✅ Nested wildcard filters:
parties[?(@.results[*].item=='A')].name - ✅ Recursive descent:
$..price - ✅ Array slices:
store.book[0:2].title
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jsonpath_sleuth-0.1.11.tar.gz.
File metadata
- Download URL: jsonpath_sleuth-0.1.11.tar.gz
- Upload date:
- Size: 16.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
513d98266b01f312611e165fcf6e1a57cee3eabf7963e71dd2ba5102ad0bbd65
|
|
| MD5 |
7c33fc36667957bd16ee31bc1c97aeff
|
|
| BLAKE2b-256 |
0f650354f57bc0c1daa773237b522e41f3516d8fa63c4169aff26212d9094539
|
File details
Details for the file jsonpath_sleuth-0.1.11-cp38-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: jsonpath_sleuth-0.1.11-cp38-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 881.6 kB
- Tags: CPython 3.8+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4949e6dee9b6ab23568441d4890093c1f6d9c013472d0510eaa47f37ea8b1cb1
|
|
| MD5 |
2e5061e0a94efe50fc0499ff240f75cc
|
|
| BLAKE2b-256 |
09ec8357ebfae1b4b6cf0ea4d8b4c0158b464e3e98d809380fa37606b69201a4
|