High-performance JSON comparison library with Zig-powered core
Project description
fastjsondiff
A high-performance Python library for comparing JSON payloads, powered by a Zig core for maximum speed.
Features
- Fast: Zig-powered core delivers sub-millisecond comparisons for typical payloads
- Complete: Detects added, removed, and changed values with full path reporting
- Deep: Handles arbitrarily nested structures (objects and arrays)
- Scalable: Efficiently processes multi-megabyte JSON files
- Pythonic: Clean API with iteration, filtering, and serialization support
Installation
pip install fastjsondiff-zig
Or using uv:
uv add fastjsondiff-zig
Development Installation
# Clone the repository
git clone https://github.com/adilkhash/fastjsondiff.git
cd fastjsondiff
# Build the Zig core
cd fastjsondiff/_zig
zig build
cd ../..
# Install in development mode
pip install -e .
# Or using uv
uv pip install -e .
Quick Start
import fastjsondiff
# Compare two JSON strings
result = fastjsondiff.compare(
'{"name": "Alice", "age": 30}',
'{"name": "Bob", "age": 30, "city": "NYC"}'
)
# Check if there are differences
if result:
print(f"Found {len(result)} differences")
# Iterate over differences
for diff in result:
print(f"{diff.type.value}: {diff.path}")
print(f" old: {diff.old_value}")
print(f" new: {diff.new_value}")
Output:
Found 2 differences
changed: root.name
old: "Alice"
new: "Bob"
added: root.city
old: None
new: "NYC"
API Reference
compare(a, b, *, array_match="index")
Compare two JSON payloads and return their differences.
Parameters:
a: First JSON input (string or bytes)b: Second JSON input (string or bytes)array_match: Array comparison strategy ("index"for position-based comparison)
Returns: DiffResult containing all differences found
Raises:
InvalidJsonError: If either input is not valid JSONTypeError: If inputs are not string or bytes
result = fastjsondiff.compare('{"a": 1}', '{"a": 2}')
compare_files(path_a, path_b, *, array_match="index", encoding="utf-8")
Compare two JSON files.
Parameters:
path_a: Path to first JSON filepath_b: Path to second JSON filearray_match: Array comparison strategyencoding: File encoding (default: utf-8)
Returns: DiffResult containing all differences found
result = fastjsondiff.compare_files("old.json", "new.json")
DiffResult
Container for comparison results.
result = fastjsondiff.compare(json_a, json_b)
# Length and boolean
len(result) # Number of differences
bool(result) # True if any differences exist
# Iteration
for diff in result:
print(diff.path)
# Index access
first_diff = result[0]
# Filtering
added = result.filter(DiffType.ADDED)
removed = result.filter(DiffType.REMOVED)
changed = result.filter(DiffType.CHANGED)
# Summary
result.summary.added # Count of added items
result.summary.removed # Count of removed items
result.summary.changed # Count of changed items
result.summary.total # Total differences
# Metadata
result.metadata.paths_compared # Number of paths visited
result.metadata.max_depth # Maximum nesting depth
result.metadata.duration_ms # Comparison time in milliseconds
# Serialization
result.to_dict() # Convert to dictionary
result.to_json() # Convert to JSON string
result.to_json(indent=2) # Pretty-printed JSON
Difference
Represents a single difference.
diff.type # DiffType.ADDED, DiffType.REMOVED, or DiffType.CHANGED
diff.path # JSON path (e.g., "root.users[0].name")
diff.old_value # Previous value (None for ADDED)
diff.new_value # New value (None for REMOVED)
diff.to_dict() # Serialize to dictionary
DiffType
Enum of difference types:
from fastjsondiff import DiffType
DiffType.ADDED # Key/element exists in second input only
DiffType.REMOVED # Key/element exists in first input only
DiffType.CHANGED # Value differs between inputs
Path Format
Paths use dot notation for object keys and bracket notation for array indices:
root.name- Object keyroot.users[0]- Array elementroot.users[0].email- Nested path
Performance
Benchmarks on typical hardware:
| Payload Size | Keys | Time |
|---|---|---|
| 2 KB | 100 | 0.2 ms |
| 23 KB | 1,000 | 2.4 ms |
| 252 KB | 10,000 | 24 ms |
| 1.3 MB | 50,000 | 122 ms |
Examples
Detecting Configuration Changes
import fastjsondiff
old_config = '{"debug": false, "port": 8080}'
new_config = '{"debug": true, "port": 8080, "host": "0.0.0.0"}'
result = fastjsondiff.compare(old_config, new_config)
for diff in result:
if diff.type == fastjsondiff.DiffType.CHANGED:
print(f"Modified: {diff.path}")
elif diff.type == fastjsondiff.DiffType.ADDED:
print(f"New setting: {diff.path}")
Comparing API Responses
import fastjsondiff
import json
response_v1 = '{"users": [{"id": 1, "name": "Alice"}]}'
response_v2 = '{"users": [{"id": 1, "name": "Alice", "email": "alice@example.com"}]}'
result = fastjsondiff.compare(response_v1, response_v2)
if result:
print("API response changed:")
print(result.to_json(indent=2))
Filtering by Change Type
import fastjsondiff
from fastjsondiff import DiffType
result = fastjsondiff.compare(old_json, new_json)
# Get only added fields
new_fields = result.filter(DiffType.ADDED)
print(f"{len(new_fields)} new fields added")
# Get only removed fields
removed_fields = result.filter(DiffType.REMOVED)
print(f"{len(removed_fields)} fields removed")
Requirements
- Python 3.10+
- Zig 0.15.2 (for building from source)
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Run tests (
pytest tests/) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
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 Distributions
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 fastjsondiff_zig-26.1.tar.gz.
File metadata
- Download URL: fastjsondiff_zig-26.1.tar.gz
- Upload date:
- Size: 209.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3931059a8811d39a1807818f87e70266bcf48b44d065ab496ec44ed1c35bbd67
|
|
| MD5 |
db731e6564a6475c234ab79740a411a6
|
|
| BLAKE2b-256 |
325ee7d1eb9983820702664805a1a6fd98bf8aaae280494cbe807f6d0a581574
|
Provenance
The following attestation bundles were made for fastjsondiff_zig-26.1.tar.gz:
Publisher:
ci.yml on adilkhash/fastjsondiff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastjsondiff_zig-26.1.tar.gz -
Subject digest:
3931059a8811d39a1807818f87e70266bcf48b44d065ab496ec44ed1c35bbd67 - Sigstore transparency entry: 831687829
- Sigstore integration time:
-
Permalink:
adilkhash/fastjsondiff@1fef18bec5b12adc1b071fbe6b929505c9aeb69a -
Branch / Tag:
refs/tags/v26.1 - Owner: https://github.com/adilkhash
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@1fef18bec5b12adc1b071fbe6b929505c9aeb69a -
Trigger Event:
push
-
Statement type:
File details
Details for the file fastjsondiff_zig-26.1-py3-none-win_amd64.whl.
File metadata
- Download URL: fastjsondiff_zig-26.1-py3-none-win_amd64.whl
- Upload date:
- Size: 158.9 kB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bb78dc76ff60e8d47f0b409b866b51db0927006a16fc6845f7321227271e463e
|
|
| MD5 |
9788e13e1a0c24624abad4406f8d3f29
|
|
| BLAKE2b-256 |
f56220f23f6c038adbc8d67f6e7b9980a4c620bd73f579a573e5dddea1bb8a6e
|
Provenance
The following attestation bundles were made for fastjsondiff_zig-26.1-py3-none-win_amd64.whl:
Publisher:
ci.yml on adilkhash/fastjsondiff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastjsondiff_zig-26.1-py3-none-win_amd64.whl -
Subject digest:
bb78dc76ff60e8d47f0b409b866b51db0927006a16fc6845f7321227271e463e - Sigstore transparency entry: 831687863
- Sigstore integration time:
-
Permalink:
adilkhash/fastjsondiff@1fef18bec5b12adc1b071fbe6b929505c9aeb69a -
Branch / Tag:
refs/tags/v26.1 - Owner: https://github.com/adilkhash
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@1fef18bec5b12adc1b071fbe6b929505c9aeb69a -
Trigger Event:
push
-
Statement type:
File details
Details for the file fastjsondiff_zig-26.1-py3-none-manylinux_2_17_x86_64.whl.
File metadata
- Download URL: fastjsondiff_zig-26.1-py3-none-manylinux_2_17_x86_64.whl
- Upload date:
- Size: 199.7 kB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2ac0c7159e5088f7944ac4379ca9c55443a9858fa6702e886efa5be32f72c291
|
|
| MD5 |
439202d5ddcdf7a205d3bad42e2d9903
|
|
| BLAKE2b-256 |
9763d78f4955105f8a9e88ae9fe7ff888c28006f212f04fb1b6e3c3cdc559852
|
Provenance
The following attestation bundles were made for fastjsondiff_zig-26.1-py3-none-manylinux_2_17_x86_64.whl:
Publisher:
ci.yml on adilkhash/fastjsondiff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastjsondiff_zig-26.1-py3-none-manylinux_2_17_x86_64.whl -
Subject digest:
2ac0c7159e5088f7944ac4379ca9c55443a9858fa6702e886efa5be32f72c291 - Sigstore transparency entry: 831687836
- Sigstore integration time:
-
Permalink:
adilkhash/fastjsondiff@1fef18bec5b12adc1b071fbe6b929505c9aeb69a -
Branch / Tag:
refs/tags/v26.1 - Owner: https://github.com/adilkhash
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@1fef18bec5b12adc1b071fbe6b929505c9aeb69a -
Trigger Event:
push
-
Statement type:
File details
Details for the file fastjsondiff_zig-26.1-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: fastjsondiff_zig-26.1-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 76.4 kB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6444e37f19d377d2bd30d1ca1b9d9ad7a01fb4c3d898b07d5b5e4e5a77adcc2
|
|
| MD5 |
e29768574de0cdb9d7dff56430cc8c15
|
|
| BLAKE2b-256 |
b3f491887def17b4e7e5258e94c3551b877cf65950911458539da3b7e1103804
|
Provenance
The following attestation bundles were made for fastjsondiff_zig-26.1-py3-none-macosx_11_0_arm64.whl:
Publisher:
ci.yml on adilkhash/fastjsondiff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastjsondiff_zig-26.1-py3-none-macosx_11_0_arm64.whl -
Subject digest:
a6444e37f19d377d2bd30d1ca1b9d9ad7a01fb4c3d898b07d5b5e4e5a77adcc2 - Sigstore transparency entry: 831687885
- Sigstore integration time:
-
Permalink:
adilkhash/fastjsondiff@1fef18bec5b12adc1b071fbe6b929505c9aeb69a -
Branch / Tag:
refs/tags/v26.1 - Owner: https://github.com/adilkhash
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@1fef18bec5b12adc1b071fbe6b929505c9aeb69a -
Trigger Event:
push
-
Statement type: