How to Compare Two JSON Files (and Ignore Key Order)
· 4 min read
To compare two JSON files reliably, compare their parsed structure rather than their text: a semantic diff ignores whitespace and key order and reports changes by path, such as $.user.email changed or $.items[2] removed. Paste both documents into the JSON diff tool, or on the command line normalize both with jq -S . and diff the results.
Why a plain text diff misleads
These two documents are equal as JSON, but diff or a Git diff shows every line as changed:
{"id": 7, "name": "Ada", "roles": ["admin", "dev"]}{
"roles": ["admin", "dev"],
"name": "Ada",
"id": 7
}JSON objects are unordered collections of name/value pairs, so key order and whitespace carry no meaning. A semantic diff parses both sides and compares values, so only real changes show up.
Text diff vs semantic diff
| Text diff | Semantic (structural) diff | |
|---|---|---|
| Whitespace and indentation | Reported as changes | Ignored |
| Key order | Reported as changes | Ignored |
| Output | Changed lines | Changed paths: added, removed, modified |
1 vs 1.0 | Different | Usually equal (same number), tool-dependent |
| Array order | Different | Different by default; some tools can ignore it |
| Works on invalid JSON | Yes | No, both sides must parse |
In the browser
The JSON diff tool parses both sides locally, then shows added, removed and changed values with their paths. That is the fastest way to compare two API responses or two versions of a config, and because nothing is uploaded it is safe for production data. If you only need a readable side-by-side text comparison, first run both documents through the formatter with "Sort keys" so ordering differences disappear.
On the command line with jq
# Normalize (sort keys, consistent indentation) and diff
diff <(jq -S . a.json) <(jq -S . b.json)
# Just ask: are they equal? Prints true or false
jq -n --slurpfile a a.json --slurpfile b b.json '$a == $b'
# Compare after removing noisy fields such as timestamps
diff <(jq -S 'del(.. | .updatedAt?)' a.json) <(jq -S 'del(.. | .updatedAt?)' b.json)<( ) is process substitution, available in bash and zsh. jq's == compares values structurally, so key order does not matter. --slurpfile wraps each file's contents in an array, which is fine for comparing whole files.
In JavaScript
import { isDeepStrictEqual } from "node:util";
import { readFileSync } from "node:fs";
const a = JSON.parse(readFileSync("a.json", "utf8"));
const b = JSON.parse(readFileSync("b.json", "utf8"));
console.log(isDeepStrictEqual(a, b)); // true if same values, key order ignoredDo not compare JSON.stringify(a) === JSON.stringify(b): stringify preserves insertion order, so equal objects with different key order compare as different. In tests, assert.deepStrictEqual and Jest or Vitest toEqual give you a readable diff of the differences.
In Python
import json
with open("a.json") as f: a = json.load(f)
with open("b.json") as f: b = json.load(f)
print(a == b) # dict comparison ignores key order
# For a path-by-path report: pip install deepdiff
from deepdiff import DeepDiff
print(DeepDiff(a, b, ignore_order=True))Python's == on dicts and lists is a deep comparison. One quirk: 1 == 1.0 and True == 1 are both true in Python, so a type change from boolean to number can slip through a plain equality check.
The array-order problem
Arrays are ordered in JSON, so [1, 2] and [2, 1] are different values, and every tool above reports them as different by default. Often that is correct (steps in a pipeline), but sometimes arrays are really sets (tags, permissions). Options:
- Sort arrays before comparing:
jq -S 'walk(if type == "array" then sort else . end)'. This is safe for arrays of scalars; arrays of objects are sorted by jq's ordering rules, which is consistent but not always meaningful. - Match objects by a key such as
idbefore comparing, so a reordered list of records does not show as wholesale changes. - Use a library option such as DeepDiff's
ignore_order=True.
Turning a diff into a patch
If you need to apply the difference elsewhere, use JSON Patch (RFC 6902), a JSON array of operations: [{"op": "replace", "path": "/user/email", "value": "new@example.com"}]. Paths use JSON Pointer syntax. Libraries such as fast-json-patch (JavaScript) and jsonpatch (Python) can generate and apply patches. For simpler merges, JSON Merge Patch (RFC 7396) sends a partial document where null means delete.
Comparing large exports? Query the relevant subset first with the JSONPath tester so the diff stays readable.
FAQ
How do I compare two JSON files online?
Paste both into a semantic JSON diff tool. It parses each side and lists added, removed and changed values by path, ignoring formatting and key order.
Does key order matter in JSON?
No. JSON objects are unordered, so {"a":1,"b":2} and {"b":2,"a":1} are equal. Array order does matter.
How do I compare JSON ignoring array order?
Sort arrays on both sides before comparing (for example with jq's walk and sort), or use a library option such as DeepDiff's ignore_order=True.
Why does git diff show my whole JSON file as changed?
The file was reformatted or its keys were reordered. Keep a stable format (for example sorted keys and 2-space indentation) or compare with a semantic diff.
What is JSON Patch?
JSON Patch (RFC 6902) is a standard format for describing changes to a JSON document as a list of add, remove, replace, move, copy and test operations.

