jsondiffview
jdv produces deterministic, JSON-shaped review text for humans. It
compares strict JSON only: the first argument is the old (before) document and
the second is the new (after) document.
The output is annotated review text, not valid JSON, JSON Patch, or an
applyable patch. Additions (+), removals (-), modifications (~), moves
(>), omissions (…), and intraline spans ([-old-], [+new+]) remain
readable without color.
The package published on PyPI is named
jsondiffview; the import package is alsojsondiffview, while the installed CLI command isjdv.Upgrading from 2.1.2? Read Migrating to 3.0.0. This source tree builds local 3.0.0 artifacts but does not publish them.
Install and run
For development:
uv sync
uv run jdv --help
Or build and install the local wheel:
uv build --no-sources
uv tool install ./dist/jsondiffview-3.0.0-py3-none-any.whl
Both entry points use the same implementation:
jdv before.json after.json
python -m jsondiffview before.json after.json
One input may be read from stdin:
cat before.json | jdv - after.json
Copy-ready PowerShell commands and editable sample documents are available in the playground.
Command
Usage: jdv [OPTIONS] OLD_JSON NEW_JSON
Compare strict OLD_JSON (before) with NEW_JSON (after).
Options:
-v, --view [summary|review|full]
Detail level. [default: review]
-k, --match-key FIELD Array-object identity key; repeat in
priority order. Any occurrence replaces
defaults: id, key, name, title.
-c, --color [auto|always|never]
Review color policy. [default: auto]
-q, --quiet Suppress the equality notice only.
--version Show the version and exit.
-h, --help Show this message and exit.
Option choices and match-key names are case-sensitive. Repeating
--match-key preserves command-line priority and replaces all default keys;
supplying the same key twice is an error. -- ends option parsing for paths
that begin with -. A file literally named - is not addressable, and both
arguments cannot use stdin.
Views
review(default) expands every changed, added, and removed value and keeps one adjacent unchanged sibling around each changed run.summarycollapses every unchanged sibling run and may summarize large wholly added or removed containers with a bounded first-two-child preview and exact omitted-descendant count.fullexpands all structural context and added/removed subtrees.
All views use the same semantic comparison. Deterministic long-string safety
limits remain active even in full.
Status and stream contract
| Status | Meaning | stdout | stderr |
|---|---|---|---|
0 |
Documents are equal | empty | No semantic differences. |
1 |
Differences were reviewed successfully | one review | empty |
2 |
Usage, input, parse, or output error | empty unless output failed | one diagnostic |
Status 1 is an expected diff-style result, not an operational failure.
--quiet suppresses only the equality notice. A closed stdout pipe exits 2
without a diagnostic or traceback.
A CI-safe shell pattern is:
set +e
jdv --color never before.json after.json
status=$?
set -e
if [ "$status" -gt 1 ]; then
exit "$status"
fi
Review format
Every physical line starts with a two-column semantic prefix. Object keys are
JSON strings; array entries use new indexes such as [2]:, while unmatched
removals use old indexes such as [old 4]:.
~ {
~ "services": [
[0]: {
"id": "api",
… 1 unchanged field omitted
},
> moved from $.services[2] to $.services[1] (matched by "id": "db")
[1]: {
"id": "db",
… 1 unchanged field omitted
},
~ [2]: {
"id": "worker",
~ "port": 9000 -> 9001
}
],
~ "title": "Hello [-world-][+team+]",
- "obsolete": true
}
Modified short strings use Unicode grapheme-safe word-, whitespace-, and
punctuation-segment spans. When one identifier or number segment has a useful
shared prefix or suffix, finer grapheme detail is used without splitting a
user-perceived character. Multiline strings are compared as exact logical
lines, with line terminators visible as JSON escapes; sufficiently similar
replacement lines can show side-specific intraline spans. Over-budget
multiline changes retain bounded first/last previews with exact omitted line
and code-point counts. Adaptive classification keeps prose inline while
routing dense separators and opaque runs to bounded hunk output. Long
single-line strings report Unicode code-point lengths, half-open offsets,
display-cell-bounded excerpts, and exact omission counts. All matching and
output limits are deterministic work counters, including in full. Strings
shown only on one side or as unchanged context are also bounded at 512 code
points, including values containing line breaks.
The main review above includes a pure move (db). Array removals retain their
old index:
~ [
[0]: "keep",
- [old 1]: "gone"
]
A moved entry can also contain a nested modification:
~ [
> moved from $[1] to $[0] (matched by "id": "b")
~ [0]: {
"id": "b",
~ "v": 1 -> 2
},
[1]: {
"id": "a",
… 1 unchanged field omitted
}
]
Ambiguous identity values remain explicit additions and removals:
~ [
+ [0]: {
+ "id": "x",
+ "v": 3
+ },
+ [1]: {
+ "id": "x",
+ "v": 4
+ },
- [old 0]: {
- "id": "x",
- "v": 1
- },
- [old 1]: {
- "id": "x",
- "v": 2
- }
]
Color is presentation only: yellow marks modifications, red removals, green
additions, and cyan move provenance. --color auto requires a terminal and an
absent or empty NO_COLOR; always overrides both redirection and
NO_COLOR; never disables ANSI. FORCE_COLOR is intentionally ignored.
Strict JSON and matching
Inputs use RFC JSON syntax with unique object names and project-owned numeric limits:
- UTF-8 and an optional leading UTF-8 BOM are accepted.
- Duplicate object keys,
NaN,Infinity, and-Infinityare rejected. - Integer literals are limited to 4,300 digits.
- Decimal/exponent lexemes are limited to 10,000 code points.
- Container nesting is limited to 256 levels; there is no global file-size limit.
- A decimal/exponent value whose binary64 conversion is non-finite, such as
1e999, is rejected. - Integer
1, decimal/exponent1.0, booleantrue, and string"1"are distinct. Decimal spellings1.0and1.00compare equal. - Object member order is ignored; array order matters.
Array entries are never paired by position or fuzzy similarity. Object entries
pair only when a configured identity value is unique on both sides. The
defaults are id, key, name, and title; use -k FIELD to replace them.
Remaining globally unique entries pair by exact strict value. Repeated exact
values may align monotonically to stabilize the review, but that alignment is
not identity evidence and never fabricates a move. Other ambiguous values
remain a removal plus an addition. Null, object, and array identity-key values
are unavailable, and a lower-priority key cannot override conflicting
higher-priority scalar identities.
Moves are the minimal deterministic relative-order changes among matched entries. Insertions and removals that merely shift absolute indexes do not mark every survivor as moved. A moved entry keeps its old/new paths and can also show nested modifications.
Development
uv sync --locked --group dev
uv run --locked pytest -q
uv run --locked ruff check .
uv run --locked ruff format --check .
uv run --locked mypy src tests
uv run --locked python -m jsondiffview --help
uv run --locked jdv --version
uv build --no-sources
The package supports CPython 3.11 and newer.
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 jsondiffview-3.0.0.tar.gz.
File metadata
- Download URL: jsondiffview-3.0.0.tar.gz
- Upload date:
- Size: 176.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cfd78cb375406bab9c234cf98c2d9e97cda8485def863bf1753b938910c74730
|
|
| MD5 |
e2005d9283c169c59e3cc7b5aaa6401a
|
|
| BLAKE2b-256 |
347c3d2f4a5aae13bb0b5dfe83cc371820e2f09a66393bab21cc940026752f7d
|
Provenance
The following attestation bundles were made for jsondiffview-3.0.0.tar.gz:
Publisher:
publish.yml on WAcry/jsondiffview
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jsondiffview-3.0.0.tar.gz -
Subject digest:
cfd78cb375406bab9c234cf98c2d9e97cda8485def863bf1753b938910c74730 - Sigstore transparency entry: 2134381018
- Sigstore integration time:
-
Permalink:
WAcry/jsondiffview@09e10d1f9ff1e2ecc429f96b88fb4105c0ffae23 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/WAcry
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@09e10d1f9ff1e2ecc429f96b88fb4105c0ffae23 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file jsondiffview-3.0.0-py3-none-any.whl.
File metadata
- Download URL: jsondiffview-3.0.0-py3-none-any.whl
- Upload date:
- Size: 40.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b3d3c6b9a0fd96f9f25f9054f993c76b65d349c1cba20c5e2978c7aba12a7bb
|
|
| MD5 |
31ff5b20c601e4de768c877552245b9d
|
|
| BLAKE2b-256 |
77024fbddf9906eaf52c8d3b28583f0ac0ce1826dfb368f8b6fdcea88333c728
|
Provenance
The following attestation bundles were made for jsondiffview-3.0.0-py3-none-any.whl:
Publisher:
publish.yml on WAcry/jsondiffview
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jsondiffview-3.0.0-py3-none-any.whl -
Subject digest:
1b3d3c6b9a0fd96f9f25f9054f993c76b65d349c1cba20c5e2978c7aba12a7bb - Sigstore transparency entry: 2134381120
- Sigstore integration time:
-
Permalink:
WAcry/jsondiffview@09e10d1f9ff1e2ecc429f96b88fb4105c0ffae23 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/WAcry
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@09e10d1f9ff1e2ecc429f96b88fb4105c0ffae23 -
Trigger Event:
workflow_dispatch
-
Statement type: