Structural comparison, not a text diff
A plain text diff flags every line that moved when someone reformats a file or reorders keys. This tool parses both documents and compares the data instead: {"a":1,"b":2} and a pretty-printed {"b": 2, "a": 1} are equal, while a value that changed from false to "yes" is reported even if it sits on an unchanged line.
Each difference appears in the Changes list with its JSONPath (such as $.settings.beta), its kind and both values. The kinds are:
- Added and Removed: a member or array item exists on one side only.
- Changed: same type, different value.
- Type changed: for example a number became a string, which usually breaks consumers even when the text looks similar.
- Moved: an array item that is unchanged but sits at a new index, or, when key order matters, a member that changed position.
Click a path to highlight its line in both editors. The chips above the list hide or show each kind, which helps when a large diff is mostly additions.
Comparison options
- Ignore key order (on by default). JSON objects are unordered, so most comparisons should ignore member order. Turn it off to audit files where order is meaningful to people, such as
package.json; reordered members are then listed as moves. - Ignore array order. Treats arrays as bags: equal items match wherever they are, so
[1, 2, 3]equals[3, 1, 2]. Items that changed are then paired with leftover items in their original order, so if a list was both reordered and edited, an edit can show as a removal plus an addition. Use it for sets such as tags or permissions, not for ordered lists like pipeline steps. - Numbers by value. By default
1.0and1count as different, because the spelling changed. With this option, numbers compare by exact decimal value, so1.0,1and10e-1are equal, without any floating-point rounding. - Ignore whitespace in strings. Strings that differ only in spaces, tabs or line breaks compare as equal, which hides noise from hand-edited descriptions.
The views and patches follow the same options, so what you choose to ignore never shows up anywhere.
How arrays are matched
Array items are aligned with the Myers algorithm, the same one git diff uses for lines, run over a fingerprint of each item. Inserting one element at the top of a 500-item list is therefore one addition, not 500 changes. Items that were removed in one place and added unchanged in another are reported as a single move, and remaining items in the same region are paired up and compared member by member, so an edited object inside a list shows the exact field that changed.
Very different arrays (more than about a thousand edits) fall back to comparing items position by position, which keeps huge inputs responsive.
Five ways to read the result
- Changes: the structural list described above.
- Side by side: both documents printed the same way (keys sorted when key order is ignored), with removed lines on the left and added lines on the right. Long unchanged stretches fold away.
- Unified diff: the same lines in the format
diff -uand code review tools use. Copy it into a ticket or download it as a.difffile. - JSON Patch: an RFC 6902 operation list that turns the original into the changed document. Try it in the JSON Patch tool.
- Merge Patch: an RFC 7396 document of only the changed members. It is easier to read, but it cannot set a value to
null(null means delete), so the tool warns when that applies.
Big numbers, YAML and JSON5
Numbers are compared digit by digit, so two 20-digit order IDs that differ only in the last digit are reported correctly; JavaScript would round both to the same value. See big integers in JSON for why that matters.
The format picker accepts JSON, JSON5 (comments and trailing commas) and YAML. Auto-detect tries strict JSON first, so you can compare a Kubernetes YAML manifest with the JSON an API returned. Syntax errors are shown at their line in the editor they belong to; the JSON validator explains them in more detail.
Examples
A user record before and after an update
One edited email, a role inserted in the middle of an array, a boolean that became a string (a type change) and a new field. The JSON Patch view shows the four operations.
{
"id": 1042,
"name": "Ada Lovelace",
"email": "ada@example.com",
"roles": ["admin", "editor"],
"settings": { "theme": "dark", "beta": false },
"createdAt": "2024-03-01T09:30:00Z"
}{
"id": 1042,
"name": "Ada Lovelace",
"email": "ada.lovelace@example.com",
"roles": ["admin", "reviewer", "editor"],
"settings": { "theme": "dark", "beta": "yes" },
"createdAt": "2024-03-01T09:30:00Z",
"lastLogin": "2024-06-12T18:04:11Z"
}[
{
"op": "replace",
"path": "/email",
"value": "ada.lovelace@example.com"
},
{
"op": "add",
"path": "/lastLogin",
"value": "2024-06-12T18:04:11Z"
},
{
"op": "add",
"path": "/roles/1",
"value": "reviewer"
},
{
"op": "replace",
"path": "/settings/beta",
"value": "yes"
}
]
JSON compared with YAML
The API returned JSON and the deployment file is YAML. Auto-detect parses both, so only the two real changes remain: replicas and the log level.
{"service": "billing", "replicas": 3, "ports": [8080, 9090], "env": {"LOG_LEVEL": "info"}}service: billing
replicas: 5
ports:
- 8080
- 9090
env:
LOG_LEVEL: debug
[
{
"op": "replace",
"path": "/env/LOG_LEVEL",
"value": "debug"
},
{
"op": "replace",
"path": "/replicas",
"value": 5
}
]
A moved array item and a respelled number
Reordered keys do not count, “build” is reported as moved from index 0 to 2, and 30.0 versus 30 is a change until you tick Numbers by value.
{"order": ["build", "test", "deploy"], "timeout": 30.0, "name": "ci"}{"name": "ci", "timeout": 30, "order": ["test", "deploy", "build"]}Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
Changed is not valid: line 4, column 3: Trailing comma before '}'Explained | One of the documents is not valid in the selected format. Strict JSON forbids a comma after the last member. | Click “Go to the error” to jump to it, remove the comma, or pick JSON5 in the format menu if comments and trailing commas are intended. |
Every number shows as changed after a re-export | The exporting tool wrote numbers differently, for example 2.50 instead of 2.5 or 1e3 instead of 1000. | Tick “Numbers by value” to compare numbers by their exact decimal value instead of their spelling. |
An edited list item appears as one removal and one addition | With “Ignore array order” on, an item that was both moved and edited has no equal partner, so it can be paired with a different leftover item. | Turn array order back on for ordered lists, or compare the item on its own by pasting just that object into both editors. |
The document is nested too deeply to process | The input nests objects or arrays thousands of levels deep, usually generated or malicious data. | Compare a smaller part of the document; real-world JSON rarely goes beyond a few dozen levels. |
Frequently asked questions
Is my data uploaded to compare it?
No. Both documents are parsed and compared by a worker inside your browser tab, and the page makes no network request with their contents. You can disconnect from the internet after the page loads and it keeps working.
Why does reordering keys not show as a difference?
In JSON, an object is an unordered set of members, so two objects with the same members are the same data. Untick “Ignore key order” if you need to see reordering, for example to review a hand-maintained file.
Can I compare a JSON file with a YAML file?
Yes. Leave the format on Auto-detect: each side is parsed on its own, and the comparison is on the data. YAML features that JSON lacks, such as anchors, are expanded first.
What is the difference between the JSON Patch and Merge Patch exports?
JSON Patch (RFC 6902) is a list of add, remove and replace operations that reproduces the change exactly, including array edits. Merge Patch (RFC 7396) is a partial document that is easier to read but replaces arrays whole and cannot set members to null.
How large can the documents be?
Several megabytes work, because the diff runs off the main thread and long unchanged stretches are folded in the line views. The change list shows the first 2,000 differences; the patches always include every change.
Does it handle numbers larger than 2^53?
Yes. Numbers are kept as their original digits, so large IDs are compared and exported exactly instead of being rounded the way JSON.parse would round them.