About JSON Compare
Comparing two JSON documents by eye is unreliable, because the things that make them look different - key order, indentation, line breaks - usually do not make them different, while the thing that matters may be a single character deep inside a nested object.
This tool compares structure and values rather than text. Key order is treated as insignificant, which is correct: the specification does not consider objects ordered, so two documents with the same pairs in a different order are equal. Array order, by contrast, is significant, so a reordered list is a real difference and is reported as one.
That distinction is the main thing to keep in mind when reading a diff. If an array of objects has merely been reordered, every element will appear changed, because position is part of an array's identity. When order genuinely does not matter in your data, sort both arrays by a stable key before comparing.
The comparison is also type-aware, which text diffing cannot be: 1 and "1" are different, as are 1 and 1.0 in documents where one side quoted its numbers. Those are exactly the mismatches that cause an integration to fail while both payloads look identical in a terminal.
Both documents stay in your browser, so comparing two real API responses does not transmit either of them.
How to use the JSON Compare
- Paste the expected document on one side and the actual one on the other.
- Read the differences: fields only in A, only in B, and fields present in both with different values.
- If an array of objects shows as entirely changed, check whether it has simply been reordered, and sort both sides by a stable key if order is not meaningful in your data.
- For values that differ only in type - 1 versus "1" - fix the serialiser rather than the comparison.
Examples
-
Side A (config v1)
{ "env": "dev", "replicas": 1 } -
Side B (config v2)
{ "env": "prod", "replicas": 3 }
JSON Compare in code
The same operation this tool performs, in the languages you are most likely to need it.
# Normalise both sides (sorted keys, canonical formatting) then diff
diff <(jq -S . a.json) <(jq -S . b.json)
# Structural equality regardless of key order
jq -n --slurpfile a a.json --slurpfile b b.json '$a == $b'
# Sort an array of objects by a stable key before comparing
jq -S '.items |= sort_by(.id)' a.json > a.norm.json
function diff(a, b, path = "$", out = []) {
if (a === b) return out;
const type = (v) => Array.isArray(v) ? "array" : v === null ? "null" : typeof v;
if (type(a) !== type(b)) {
out.push({ path, from: a, to: b, reason: "type" });
return out;
}
if (type(a) === "object") {
for (const k of new Set([...Object.keys(a), ...Object.keys(b)])) {
if (!(k in a)) out.push({ path: `${path}.${k}`, to: b[k], reason: "added" });
else if (!(k in b)) out.push({ path: `${path}.${k}`, from: a[k], reason: "removed" });
else diff(a[k], b[k], `${path}.${k}`, out);
}
} else if (type(a) === "array") {
for (let i = 0; i < Math.max(a.length, b.length); i++) {
diff(a[i], b[i], `${path}[${i}]`, out);
}
} else {
out.push({ path, from: a, to: b, reason: "value" });
}
return out;
}
// Note: comparing JSON.stringify(a) === JSON.stringify(b) is NOT
// a structural comparison - it is sensitive to key order.
import json
# Python dicts compare structurally, so key order is already ignored
a, b = json.loads(raw_a), json.loads(raw_b)
print(a == b)
def diff(a, b, path="$"):
if type(a) is not type(b):
yield (path, a, b, "type"); return
if isinstance(a, dict):
for k in a.keys() | b.keys():
if k not in a: yield (f"{path}.{k}", None, b[k], "added")
elif k not in b: yield (f"{path}.{k}", a[k], None, "removed")
else: yield from diff(a[k], b[k], f"{path}.{k}")
elif isinstance(a, list):
for i in range(max(len(a), len(b))):
yield from diff(a[i] if i < len(a) else None,
b[i] if i < len(b) else None, f"{path}[{i}]")
elif a != b:
yield (path, a, b, "value")
for path, x, y, why in diff(a, b):
print(f"{why:8} {path}: {x!r} -> {y!r}")
When you need this
- Finding out what actually changed between an expected and an actual API response in a failing test.
- Comparing configuration between two environments to explain a behaviour difference.
- Checking whether an API version bump changed the response shape.
- Verifying that a migration or transformation preserved every field.
- Confirming two systems that should agree really do, byte differences aside.
Common problems and what causes them
- Comparing with JSON.stringify on both sides
- That is a text comparison, so it reports a difference whenever key order differs even though the documents are structurally identical. Compare structurally, or sort keys recursively before stringifying.
- An entire array showing as changed
- Array order is significant in JSON, so a reordered list is genuinely a different document. If order does not carry meaning in your data, sort both arrays by a stable key first - jq -S '.items |= sort_by(.id)' does it.
- Values that differ only by type
- 1 and "1" are different values, as are true and "true". These slip past visual inspection and are a classic cause of an integration failing against a payload that looks correct. Fix the serialiser that quoted them.
- Floating-point values that look equal
- 0.1 + 0.2 does not serialise as 0.3, and two systems computing the same figure can differ in the last bits. For money, compare integer minor units rather than floats.
- Null versus absent
- {"a": null} and {} are different documents, and APIs often treat them differently - one clears a field, the other leaves it untouched. A diff that collapses the two hides a real bug.
- Large integers already mangled before comparison
- If both documents pass through a double-based parser, two different snowflake ids above 2^53 can arrive identical. Compare the raw text for those fields, or parse with bigint support.
FAQ
- Does key order matter when comparing JSON?
- No. JSON objects are unordered by specification, so the same pairs in a different order are the same document. This tool ignores key order. Array order, however, is significant and is reported.
- Why does my whole array show as different?
- Because the elements moved. Position is part of an array's identity in JSON, so a reordered list is a real difference. Sort both arrays by a stable key before comparing if order is not meaningful in your case.
- Is 1 the same as "1"?
- No - one is a number and the other a string, and any type-aware comparison reports it. This is a common and easily missed cause of integration failures, usually introduced by a serialiser that quotes numeric fields.
- How do I compare two JSON files on the command line?
- diff <(jq -S . a.json) <(jq -S . b.json) normalises key order and formatting first, so the diff shows real changes. For a plain equal/not-equal answer, jq -n --slurpfile a a.json --slurpfile b b.json '$a == $b'.
- Are my documents uploaded?
- No. The comparison runs entirely in your browser, so pasting two real API responses does not transmit them anywhere.
- Is key order ignored?
- JSON objects are compared by key semantics, not source key order. Arrays are compared by index.
- Can I compare UUID strings?
- Yes. Two valid UUIDs in the same format are compared as 16-byte values.
Related reading
- How to pretty-print JSON
- Common JSON validation errors
- Text diff line-by-line instead