A Python package for handling nested JSON structures.
Project description
๐งญ JSONavigator
The Swiss Army knife for nested JSON โ traverse, flatten, search, validate, format, and diff JSON structures with a single lightweight Python library.
๐ค Why JSONavigator?
Working with deeply nested JSON in Python is painful. Standard dict access breaks on missing keys; jmespath has a custom query language to learn; pandas is overkill for simple lookups.
JSONavigator solves this with a zero-dependency, pure-Python code that covers the whole lifecycle of nested JSON manipulation โ from traversal to diffing โ in a handful of intuitive functions.
4,000+ downloads โข Zero dependencies โข Python 3.8+ โข MIT Licensed
โจ Features
| Feature | Function | Description |
|---|---|---|
| ๐ Traverse | traverse_json |
Recursively yield every (path, value) pair |
| ๐ฆ Flatten | flatten_json |
Collapse nested JSON into a single-level dict |
| ๐ฏ Get by path | get_value_at_path |
Retrieve a value using a dot-notation path |
| โ๏ธ Set by path | set_value_at_path |
Update or set a value at a dot-notation path |
| ๐๏ธ Delete by path | delete_at_path |
Remove a key/index at a dot-notation path |
| ๐งน Delete key | delete_key |
Recursively remove every occurrence of a key |
| ๐ Find value | find_value_of_element |
Search for first occurrence of a key, anywhere |
| ๐บ๏ธ Find all paths | find_all_paths_for_element |
Get every path where a key exists |
| ๐งน Clear values | empty_all_the_values |
Reset all leaf values to "" in-place |
| โ Validate path | validate_path |
Assert a path string is well-formed |
| ๐ Format path | format_path |
Pretty-print a path for human readability |
| ๐ Compare JSON | compare_files |
Diff two JSON objects or files with a summary |
| ๐จ Custom exceptions | InvalidPathError, ElementNotFoundError |
Semantic error handling |
๐ฆ Installation
pip install jsonavigator
From source:
git clone https://github.com/Nikhil-Singh-2503/JSONavigator.git
cd JSONavigator
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
Requires: Python โฅ 3.8. No runtime dependencies.
โก Quick Start
from jsoninja import (
traverse_json,
get_value_at_path,
flatten_json,
find_value_of_element,
find_all_paths_for_element,
empty_all_the_values,
validate_path,
format_path,
compare_files,
)
data = {
"user": {
"name": "Alice",
"scores": [95, 87, 100],
"address": {"city": "Berlin", "zip": "10115"}
}
}
# Traverse every leaf
for path, value in traverse_json(data):
print(f"{path}: {value}")
# user.name: Alice
# user.scores[0]: 95 ...
# Retrieve by path
print(get_value_at_path(data, "user.address.city")) # Berlin
# Flatten to single level
flat = flatten_json(data)
# {"user.name": "Alice", "user.scores[0]": 95, ...}
๐ Usage Guide
1๏ธโฃ Traverse Nested JSON
View Details
Yields (path, value) tuples for every leaf node. Supports mixed dicts and lists.
from jsoninja import traverse_json
data = {"a": {"b": [1, 2], "c": 3}}
for path, value in traverse_json(data):
print(f"Path: {path}, Value: {value}")
Path: a.b[0], Value: 1
Path: a.b[1], Value: 2
Path: a.c, Value: 3
Use the
separatorparameter to change the default.delimiter.traverse_json(data, separator="/") # a/b[0], a/b[1], a/c
2๏ธโฃ Get Value at a Specific Path
View Details
from jsoninja import get_value_at_path
data = {"a": {"b": [10, 20, 30]}}
print(get_value_at_path(data, "a.b[2]")) # 30
print(get_value_at_path(data, "a.b[0]")) # 10
3๏ธโฃ Set Value at a Specific Path
View Details
Update or create a leaf value using dot notation, modifying the dict in place.
from jsoninja import set_value_at_path
data = {"user": {"scores": [10, 20]}}
set_value_at_path(data, "user.scores[1]", 99)
print(data) # {"user": {"scores": [10, 99]}}
4๏ธโฃ Delete Value at a Specific Path
View Details
Delete a key or pop an index at the specified path.
from jsoninja import delete_at_path
data = {"a": {"b": 1, "c": 2}}
delete_at_path(data, "a.b")
print(data) # {"a": {"c": 2}}
5๏ธโฃ Flatten JSON
View Details
Collapses any depth of nesting into a flat {path: value} dictionary.
from jsoninja import flatten_json
data = {"a": {"b": [1, 2], "c": 3}}
print(flatten_json(data))
{
"a.b[0]": 1,
"a.b[1]": 2,
"a.c": 3
}
6๏ธโฃ Find a Value by Key
View Details
Returns the first occurrence of a key anywhere in the structure.
from jsoninja import find_value_of_element
data = {"a": {"b": {"c": 42}}}
print(find_value_of_element("c", data)) # 42
print(find_value_of_element("z", data)) # "" (not found)
7๏ธโฃ Find All Paths for a Key
View Details
Returns every path where the target key appears โ great for duplicate-key analysis.
from jsoninja import find_all_paths_for_element
data = {"a": 1, "b": {"a": 2}, "c": [{"a": 3}]}
print(find_all_paths_for_element(data, "a"))
# ["a", "b.a", "c[0].a"]
8๏ธโฃ Empty All Values
View Details
Resets every leaf value to "" โ useful for creating JSON templates or anonymising data.
from jsoninja import empty_all_the_values
data = {
"a": 1,
"b": {"c": 42, "d": [1, 2, {"e": "hello"}]},
}
print(empty_all_the_values(data))
{
"a": "",
"b": {"c": "", "d": ["", "", {"e": ""}]}
}
9๏ธโฃ Recursively Delete a Key
View Details
Recursively remove every occurrence of a key name anywhere in the nested object.
from jsoninja import delete_key
data = {"id": 1, "user": {"id": 2, "name": "Alice"}}
delete_key(data, "id")
print(data) # {"user": {"name": "Alice"}}
๐ Validate & Format Paths
View Details
from jsoninja import validate_path, format_path
from jsoninja.exceptions import InvalidPathError
# Validation
try:
validate_path("a.b[1]") # returns True
validate_path("a.b[1") # raises InvalidPathError (mismatched brackets)
except InvalidPathError as e:
print(f"Error: {e}")
# Human-readable formatting
print(format_path("user.address.city")) # user -> address -> city
print(format_path("a.b[1]")) # a -> b[1]
1๏ธโฃ1๏ธโฃ Compare Two JSON Structures
View Details
Diff two JSON objects or files and receive a structured list of changes plus a summary.
from jsoninja import compare_files
json1 = {"a": {"b": 1, "c": 2}, "d": [1, 2]}
json2 = {"a": {"b": 1, "c": 99}, "d": [1, 2, 3], "e": "new"}
changes, summary = compare_files(json1, json2)
print(summary)
# {"added": 2, "removed": 0, "changed": 1, "type_changed": 0, "unknown": 0}
for change in changes:
print(change)
# {"type": "changed", "path": "a-->c", "old_value": 2, "new_value": 99}
# {"type": "added", "path": "d[2]", "new_value": 3}
# {"type": "added", "path": "e", "new_value": "new"}
Compare from file paths:
changes, summary = compare_files(
r"path/to/old.json",
r"path/to/new.json",
isPath=True
)
Note: When passing file paths as strings, use raw strings (
r"...") or double backslashes (\\) to avoid escape-sequence issues.
Change types returned:
| Type | Meaning |
|---|---|
added |
Key/value exists in JSON 2 but not JSON 1 |
removed |
Key/value exists in JSON 1 but not JSON 2 |
changed |
Same path, different value |
type_changed |
Same path, different Python type |
๐๏ธ Project Structure
JSONavigator/
โโโ jsoninja/
โ โโโ __init__.py # Public API surface
โ โโโ core.py # traverse, get, find, empty functions
โ โโโ utils.py # flatten, validate, format helpers
โ โโโ compare.py # JSON diff engine
โ โโโ exceptions.py # Custom exception hierarchy
โโโ tests/
โ โโโ test_core.py # 30+ core function tests
โ โโโ test_compare.py # Compare module tests
โ โโโ test_utils.py # Utility function tests
โ โโโ conftest.py # pytest fixtures
โโโ .github/workflows/
โ โโโ publish-to-pypi.yml # Automated PyPI release CI
โโโ requirements.txt # Dev/test dependencies
โโโ setup.py
โโโ README.md
๐ ๏ธ Package Reference
jsoninja.core
| Function | Signature | Returns |
|---|---|---|
traverse_json |
(data, parent_key='', separator='.') |
Generator of (path, value) |
get_value_at_path |
(data, path, separator='.') |
Value at the path |
set_value_at_path |
(data, path, new_value, separator='.') |
Mutated data |
delete_at_path |
(data, path, separator='.') |
Mutated data |
delete_key |
(data, target_key) |
Mutated data |
find_value_of_element |
(target_key, data) |
First matched value or "" |
find_all_paths_for_element |
(file_data, target_key, path='', separator='.') |
list[str] of all paths |
empty_all_the_values |
(data) |
Mutated data with "" leaves, or None |
jsoninja.utils
| Function | Signature | Returns |
|---|---|---|
flatten_json |
(data, parent_key='', separator='.') |
Flat dict |
validate_path |
(path, separator='.') |
True or raises InvalidPathError |
format_path |
(path, separator='.') |
Human-readable path string |
jsoninja.compare
| Function | Signature | Returns |
|---|---|---|
compare_files |
(file1, file2, separator='-->', isPath=False) |
(changes_list, summary_dict) |
jsoninja.exceptions
| Exception | When raised |
|---|---|
InvalidPathError |
Malformed path string passed to validate_path / format_path |
ElementNotFoundError |
Target element absent from JSON structure |
JSONStructureError |
Unexpected JSON structure type |
๐ก Use Cases
- ๐ฌ API response inspection โ quickly explore the shape of deeply nested API payloads.
- ๐งช Test assertion helpers โ flatten and validate JSON responses in pytest fixtures.
- ๐ Config file diffing โ compare application config files between environments.
- ๐๏ธ JSON schema generation โ traverse and collect all paths to infer structure.
- ๐ต๏ธ Data anonymisation โ
empty_all_the_valuesstrips sensitive data for safe sharing. - ๐ฅ ETL pipelines โ flatten nested JSON before inserting into relational databases.
๐ง Development Setup
# 1. Clone the repository
git clone https://github.com/Nikhil-Singh-2503/JSONavigator.git
cd JSONavigator
# 2. Create and activate a virtual environment
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# 3. Install development dependencies
pip install -r requirements.txt
# 4. Run the tests
pytest
# 5. Run with coverage report
pytest --cov=jsoninja --cov-report=term-missing
๐งช Testing
The test suite covers all public functions with 30+ unit tests using pytest.
# All tests
pytest
# Specific module
pytest tests/test_core.py
pytest tests/test_compare.py
pytest tests/test_utils.py
# With HTML coverage report
pytest --cov=jsoninja --cov-report=html
๐ค Contributing
Contributions are warmly welcome! Here's how to get started:
- Fork the repository on GitHub.
- Clone your fork:
git clone https://github.com/<your-username>/JSONavigator.git
- Create a feature branch:
git checkout -b feature/my-feature
- Make changes and add tests for any new functionality.
- Run tests to make sure everything passes:
pytest
- Commit and push your changes:
git commit -m "feat: add my-feature" git push origin feature/my-feature
- Open a Pull Request against the
mainbranch.
Please follow the existing code style and write docstrings for any new functions.
๐ Roadmap
-
set_value_at_pathโ update a value at a given path in place -
delete_keyโ remove a key anywhere in the structure - JSONPath (
$) query syntax support - Async-friendly streaming traversal for large JSON files
- Type-annotated stubs (
py.typedmarker) -
compare_filesโ side-by-side HTML diff report
๐ License
This project is licensed under the MIT License โ see the LICENSE file for details.
๐ค Author
Nikhil Singh
- ๐ง Email: nikhilraj7654@gmail.com
- ๐ GitHub: @Nikhil-Singh-2503
- ๐ฆ PyPI: jsonavigator
โญ Star the Repo
If JSONavigator saves you time, please consider giving it a โญ on GitHub โ it helps others discover the project!
Project details
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