Structural diff for two JSON files — see which values changed, by path, ignoring key order and whitespace. Zero dependencies.
Project description
jdelta
A structural diff for two JSON files. git diff and diff work on lines —
so reformatting, reordered keys, or a changed indent drown the one value you
actually care about in red-and-green noise. jdelta compares the data, not
the text, and tells you exactly which values changed, addressed by path. Zero
dependencies, no network.
pip install jdelta
$ jdelta before.json after.json
Added (2)
+ email "alice@corp.com"
+ user.tags[2] "d"
Removed (1)
- legacyId 1001
Changed (3)
~ user.age 30 → 31
~ user.role "viewer" → "admin"
~ user.tags[1] "b" → "c"
+2 -1 ~3
This is the Python build. A behavior-equivalent Node build is on npm:
npx jdelta(https://github.com/jjdoor/jdelta).
Why
You're reviewing a config change, an API-response snapshot, a tsconfig, a
locale file — and the diff is unreadable because someone ran a formatter or the
serializer reordered keys. You don't care that line 40 moved to line 12; you
care that auth.required flipped to false. jdelta ignores key order and
whitespace entirely and reports the actual value-level delta by path.
Usage
jdelta old.json new.json # human-readable, grouped by added/removed/changed
jdelta old.json new.json --json # machine-readable
jdelta old.json new.json --quiet # just the +a -r ~c summary line
jdelta old.json new.json --exit-code # exit 1 if they differ (CI gate)
Options
| Flag | Effect |
|---|---|
--json |
Emit { added, removed, changed, summary } as JSON |
--quiet |
Print only a one-line summary (+a -r ~c, or no differences) |
--exit-code |
Exit 1 when the files differ (for CI gates) |
-v, --version |
Print version |
-h, --help |
Show help |
How it reads the diff
- Paths. Object keys use dot notation (
user.profile.age); array elements use index notation (items[2].price). Keys that aren't plain identifiers — containing dots, spaces or hyphens, or starting with a digit — fall back to quoted brackets:["order-id"],["123"]. - Added / Removed / Changed. A key present on only one side is added or
removed; a key on both with a different value is changed. A type change
(
number→string,object→array) is reported as a single changed entry tagged with the kinds — not as an add + remove. - Arrays are compared by index.
xs[2]is compared toxs[2]; a length change shows up as added/removed trailing elements. (Inserting at the front of an array therefore reads as "everything shifted" — index diffing is simple and predictable rather than guessing at moves.) - Numbers are compared by value (
1and1.0are equal). Two caveats follow from how each runtime parses JSON numbers:- Float display. Floats are rendered by each runtime's native serializer, so
an integral float can show as
1(Node) or1.0(Python), and exotic floats (e.g.1e-7) may differ in formatting. The detected change is the same. - Large integers. JavaScript has no bigint in JSON, so the Node build parses
every number as an IEEE-754 double: integers beyond ±2^53 (snowflake IDs,
int64 database keys) lose precision and can compare equal when they aren't —
so the Node build may miss a change to such a value (and
--exit-codewon't fire). The Python build keeps integer precision exactly — prefer it for large-integer data.
- Float display. Floats are rendered by each runtime's native serializer, so
an integral float can show as
- Long values are abbreviated in the human view (truncated with
…past ~72 characters).--jsonoutput is never truncated.
--json shape
{
"added": [{ "path": "email", "value": "alice@corp.com" }],
"removed": [{ "path": "legacyId", "value": 1001 }],
"changed": [{ "path": "user.age", "from": 30, "to": 31 }],
"summary": { "added": 1, "removed": 1, "changed": 1, "total": 3 }
}
Exit codes
| Code | Meaning |
|---|---|
0 |
success (default — even when the files differ) |
1 |
files differ and --exit-code was passed |
2 |
error (bad args, unreadable file, invalid JSON) |
By default jdelta is a viewer and exits 0; add --exit-code to make it a
gate (the git diff --exit-code convention).
License
MIT
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 jdelta-0.1.0.tar.gz.
File metadata
- Download URL: jdelta-0.1.0.tar.gz
- Upload date:
- Size: 10.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a91f1eaaf9e4e17c5a30f206c715a4edb06f562dcdb6af3adebd77678ea7961a
|
|
| MD5 |
e642b2c627f14cae6cab3b93493bde64
|
|
| BLAKE2b-256 |
45ac542e74aa990252f7e5df2c460a044fa8296588efee6049dd510838dbdc33
|
File details
Details for the file jdelta-0.1.0-py3-none-any.whl.
File metadata
- Download URL: jdelta-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2593b897c66401308c72b30853e4cc85d2ca53cc232754cf73df1ddc12c6a91
|
|
| MD5 |
4117bea2b119fd44366ba12c1fd1eddf
|
|
| BLAKE2b-256 |
0d88cd9aecebd154ddd4dd3f8331957975962e9f76d89c60585d0f2a5bfdc86c
|