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
Metadata
Release files for jdelta 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jdelta-0.1.0.tar.gz | 10.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jdelta-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 19.1 kB
Release files / jdelta-0.1.0.tar.gz
| Download URL | jdelta-0.1.0.tar.gz |
|---|---|
| Size | 10.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a91f1eaaf9e4e17c5a30f206c715a4edb06f562dcdb6af3adebd77678ea7961a
|
|
BLAKE2b-256 checksum How to use checksums |
45ac542e74aa990252f7e5df2c460a044fa8296588efee6049dd510838dbdc33
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.7
|
Release files / jdelta-0.1.0-py3-none-any.whl
| Download URL | jdelta-0.1.0-py3-none-any.whl |
|---|---|
| Size | 8.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e2593b897c66401308c72b30853e4cc85d2ca53cc232754cf73df1ddc12c6a91
|
|
BLAKE2b-256 checksum How to use checksums |
0d88cd9aecebd154ddd4dd3f8331957975962e9f76d89c60585d0f2a5bfdc86c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.7
|