JSON Diff
Compare two JSON snippets; list added, removed and changed key paths
JSON Diff: Why a Text Diff of JSON Floods the Screen With Noise
The gap between a text diff and a structural diff
Comparing two JSON bodies with a plain text diff tool produces two distinct problems:
- Formatting differences are read as content differences. If A is indented with two spaces and B is minified to one line, the text diff concludes the whole thing changed, even though the data is identical.
- Key-order differences are read as content differences. JSON objects are unordered (the spec says so), so
{"a":1,"b":2}and{"b":2,"a":1}mean the same thing, yet a text diff flags two deletions and two insertions.
A structural diff instead parses both sides into objects before comparing: it ignores whitespace and key order and reports a difference only at a real path. The output takes the form "which path, and what it changed from and to", which is far more useful than a line number — especially inside a deeply nested config or an API response, where a line number tells you almost nothing.
Four judgment rules that must be stated explicitly
When comparing JSON, several cases where "looks the same" and "is the same" diverge, and the tool must pick a rule and say so:
| Case | Counts as difference? | Note |
|---|---|---|
1 vs 1.0 | usually no | equal as numbers, but different as text |
1 (number) vs "1" (string) | yes | different types; in JSON these are two distinct values |
key absent vs value null | yes | "no such field" and "field is empty" differ in meaning, especially in APIs |
true vs "true" | yes | boolean and string must not be mixed |
The third row is the most common point of dispute. In specifications such as JSON Merge Patch (RFC 7396), null is used to mean "delete this key" precisely because these two states must be distinguishable.
The special difficulty of comparing arrays
Objects have keys to align on, arrays do not. So the change from [1,2,3] to [3,2,1] admits two readings:
- The elements were reordered — if the array is itself an ordered set (a time-sorted user list, say), this is a reshuffle.
- Each of the three elements was replaced — if you treat the array as an unordered set, none of the content actually changed.
The tool cannot know your intent, so it must commit to one algorithm. A common choice is longest common subsequence (LCS), which identifies "these elements stayed, these were inserted or deleted" and behaves best when an element is inserted in the middle — exactly the most common change in practice (adding one item to a list). A naive comparison by index will, on an insertion, flag every later element as changed, producing enormous noise.
There is a further layer: if the array elements are objects that carry a known unique key (an id, for example), aligning by id is far more accurate than aligning by position. This is something a generic structural algorithm cannot do; it requires tailoring to your data shape.
What this tool cannot do
- It does not understand a semantic rename. Renaming
userNametousernamecan only be reported as "one key deleted, one key added"; the tool has no idea it was a rename. - It does not validate structural correctness. Both sides must already be valid JSON for a comparison to run; a structure that violates your API contract will not be caught — that needs JSON Schema.
- It has size limits. A structural diff's cost rises sharply with deep nesting and large arrays. For JSON of several megabytes, trim to the subtree you care about with a tool such as jq before comparing.
A practical workflow
- First confirm both sides are valid JSON (this tool reports a parse failure directly instead of yielding a wrong comparison).
- Read the path of a difference, not its position. A path tells you directly "one item in the config changed".
- When an array shows a mass of differences, first ask whether the element order changed, or whether alignment should be by a unique key.
- If the result is for a diff review, sort the keys on both sides first (with a key sorter) to suppress key-order noise.
FAQ
How are array order changes counted?
Elements are compared by the same index. If an element just moved position, the old and new values at that index differ, so it is marked as changed.
Does diff ignore field order?
Yes. Object keys in a different order but with the same content count as no difference; only added, removed or changed values are listed.