A JSON diff that reads the structure, not the characters
This JSON diff parses both documents and compares the values, so what you get back is a list of
paths — orders[2].status, limits["rate/min"] — with the old value and the new one, plus the same
change expressed as a JSON Patch you can paste into a request. Reformatting, reordered keys and a
number written two ways stop being findings, because none of them changes what a consumer reads.
Paste an original on the left and a changed version on the right. Nothing is compared until both sides parse; if one of them does not, you get the line and column where it gave up rather than a generic complaint.
Why comparing JSON as text misleads you
Every JSON serialiser makes choices that the format does not fix. Key order is one: JSON.stringify
preserves insertion order, Python's json.dumps can sort, Go's encoding/json sorts map keys
always, and a database export may follow the column order. Indentation is another, along with
whether non-ASCII characters are escaped and whether a float keeps its trailing zero.
Run a line-based diff across two files that disagree on all four and you get a wall of red and green describing nothing. Worse is the opposite failure: a genuine change buried inside three hundred cosmetic lines, which is how a modified timeout or a flipped boolean gets approved in review.
The structural comparison here fixes the rules in advance. Key order never counts. Array order counts
by default, because position is meaningful in a sequence. Numbers compare by value unless you turn
that off, in which case 1 and 1.0 become a reported difference — useful when you are chasing a
serialiser that changed, and noise when you are not.
What a change looks like
Three kinds of finding, and no others. Added means the path exists on the right and not on the
left. Removed is the reverse. Changed covers a different value at the same path, including a
value that changed type — "3" where there used to be 3 is a single change, not a removal plus an
addition, because that is the bug you are usually looking at.
A missing key and a key set to null are never conflated. Removing owner and setting
"owner": null are different messages to whatever consumes the document: one says the field is gone,
the other says it exists and is empty. Tools that collapse the two hide schema migrations.
Paths use the notation you would type in code, so you can copy one straight into a console. Keys that
are not valid identifiers are bracketed and quoted instead, which keeps data["first name"]
unambiguous.
The patch, and where it goes
The same result comes out as an RFC 6902 patch: an array of add, remove and replace operations,
each addressing a location with an RFC 6901 pointer. That is the body of an HTTP PATCH with the
media type application/json-patch+json, and it is what a partial-update endpoint expects instead of
a full replacement.
Two details in the generated patch are deliberate. Removals from an array are emitted from the
highest index downwards, because applying them in the other order shifts every index still to come —
remove /items/1 first and the old /items/2 is now /items/1. And an addition to an array whose
order you told the tool to ignore uses the append pointer /- rather than a position, since with
order declared irrelevant any position would be invented information.
Where it stops being useful
An array of objects with no identity field is the honest limit. Comparing by index means one inserted element at the front reports every subsequent element as changed, and no amount of cleverness fixes that without knowing which field identifies a row. Sort both sides by their key before comparing, or compare the subtrees you care about one at a time.
Very large documents are the other limit. Past five hundred differences the list stops, on the theory that a diff nobody can read is not a result. When two files disagree that thoroughly, they are usually not versions of each other, and the useful question is about a smaller subtree.
Where this fits
If one of the two sides will not parse, the JSON formatter is the faster way to find out why, since it validates and indents in the same pass. When you need to prove a change to someone reading a pull request, the text diff still has its place — it shows the file as they will see it. And when the two documents come from different systems that speak different formats, the JSON to YAML converter puts both into the same shape before you start comparing them.
Everything above happens inside your browser tab, with no upload and no round trip.