Why Your JSON Diff Tool Gives False Positives (and How to Fix It)
JSON diff tools flag changes that aren't real — reordered keys, reformatted arrays, whitespace. Here's why it happens and how to get a clean, accurate diff.
You run a JSON diff and it says everything changed. But you know nothing changed — you just reformatted the file, or your API returned the same data with properties in a different order. The tool is lying to you.
These are false positives: differences that look real but aren’t. They’re one of the most common frustrations when debugging JSON, and they’re almost always caused by using the wrong type of diff.
This post explains exactly why JSON diff tools produce false positives, which scenarios trigger them, and how to get a clean, accurate comparison every time.
TL;DR: Most false positives come from text-based diff tools that don’t understand JSON structure. Switch to a semantic diff tool like GoGood.dev JSON Compare — it parses both objects first, so reordered keys and reformatted values never show as changes.
What are false positives in a JSON diff?
A false positive is when a diff tool reports a difference between two JSON objects that are semantically identical — they represent the same data, but look different as text.
The diff tool says: “these are different.” The actual data says: “these are identical.”
That’s a false positive. It wastes your time, it pollutes your diff output, and it can cause automated checks to fail on a non-issue.
Why JSON diff tools produce false positives
The root cause is almost always the same: the tool is doing a text diff, not a JSON diff.
A text diff compares the raw characters in two files, line by line. It has no concept of what JSON is. It doesn’t know that {"a":1,"b":2} and {"b":2,"a":1} are the same object — it just sees two different strings.
A JSON diff parses both inputs into data structures first, then compares them semantically. Key order doesn’t matter. Whitespace doesn’t matter. Only the actual data matters.
Most terminal tools (diff, git diff) and generic text editors do text diffs. If you’re using one of those on JSON files, you’ll get false positives whenever the representation changes without the data changing.
The four most common false positive scenarios
1. Key reordering
This is the most frequent cause. JSON objects are unordered — the spec explicitly says key order is not significant. But many APIs, serialization libraries, and code formatters sort keys differently or preserve insertion order inconsistently.
// Response from server A
{"userId": 42, "email": "dev@example.com", "role": "admin"}
// Response from server B (same data, different order)
{"role": "admin", "userId": 42, "email": "dev@example.com"}
A text diff flags every line as changed. A JSON diff sees two identical objects.
2. Whitespace and formatting differences
Minified JSON versus pretty-printed JSON looks completely different as text. So does 2-space indentation versus 4-space. These are purely cosmetic — the data is identical.
// Minified
{"name":"Alice","scores":[10,20,30]}
// Pretty-printed
{
"name": "Alice",
"scores": [
10,
20,
30
]
}
Every single line is “different” in a text diff. In a JSON diff: zero differences.
3. Array reordering (when order doesn’t matter)
Some arrays are ordered by definition — a list of steps, a sequence of events. Others are inherently unordered — a list of permissions, a set of tags, a collection of IDs. When an unordered array comes back in a different sequence, a text diff flags every element as changed.
// Original
{"permissions": ["read", "write", "delete"]}
// Modified (same permissions, different order)
{"permissions": ["delete", "read", "write"]}
Text diff: three lines changed. Semantic diff with “ignore array order”: zero differences.
4. Numeric representation differences
1.0 and 1 are the same number. 1e3 and 1000 are the same number. A text diff doesn’t know this — it sees different strings.
// Response 1
{"timeout": 1000, "ratio": 1.0}
// Response 2
{"timeout": 1e3, "ratio": 1}
These are numerically identical but textually different. A JSON-aware diff tool that compares parsed values handles this correctly.
How to fix JSON diff false positives
Fix 1: Use a structural JSON diff tool
The cleanest solution is to stop using text-based diff tools on JSON and use a tool that understands the format.
Open gogood.dev/json-compare, paste both JSON objects, and the diff is structural — it parses both inputs and compares the data, not the text. Reordered keys never show as changes.
The result shows only what actually changed — not what the text representation looks like.
Fix 2: Normalise before diffing with jq
If you’re working on the command line, jq can normalise both files before diffing:
diff <(jq --sort-keys . file1.json) <(jq --sort-keys . file2.json)
The --sort-keys flag sorts all keys alphabetically at every nesting level before outputting. This eliminates key-ordering false positives. Combined with jq’s formatting, it also eliminates whitespace differences.
For arrays where order doesn’t matter, you can sort them too:
diff \
<(jq --sort-keys '.permissions |= sort' file1.json) \
<(jq --sort-keys '.permissions |= sort' file2.json)
Fix 3: Normalise in code
If you’re comparing JSON programmatically in a test or script, use your language’s JSON parser to normalise before comparing:
// Instead of: fileContents1 === fileContents2
// Do this:
const a = JSON.parse(fileContents1);
const b = JSON.parse(fileContents2);
function isDeepEqual(x, y) {
if (typeof x !== typeof y) return false;
if (typeof x !== 'object' || x === null) return x === y;
if (Array.isArray(x) !== Array.isArray(y)) return false;
if (Array.isArray(x)) {
if (x.length !== y.length) return false;
return x.every((item, i) => isDeepEqual(item, y[i]));
}
const keysA = Object.keys(x).sort();
const keysB = Object.keys(y).sort();
if (keysA.join() !== keysB.join()) return false;
return keysA.every(k => isDeepEqual(x[k], y[k]));
}
console.log(isDeepEqual(a, b)); // true for same data, regardless of key order
In Python, json.load() and == already do deep structural comparison:
import json
with open('file1.json') as f1, open('file2.json') as f2:
a, b = json.load(f1), json.load(f2)
print(a == b) # True if data is identical, regardless of key order
Fix 4: Enable “Ignore Array Order” for unordered collections
If your JSON contains arrays where order is not semantically significant (tags, permissions, IDs), look for an “Ignore Array Order” option in your diff tool. GoGood.dev JSON Compare includes this option — enable it and arrays are sorted before comparison, eliminating that entire category of false positives.
When a “false positive” is actually a real difference
One important caveat: for some arrays, order does matter. A list of pipeline stages, a sequence of middleware, an ordered set of rules — these should flag positional changes as real differences.
Don’t blanket-enable “ignore array order” without thinking about your specific data. For arrays where order is significant, positional changes are real changes and your diff tool is correct to report them.
The mental model: if you’d represent the data as a Set in code, order doesn’t matter. If you’d represent it as an Array with meaningful sequence, order does matter.
FAQ
Why does git diff show so many changes in my JSON files?
git diff is a text diff — it compares lines of text, not JSON structure. Any change in formatting, key order, or whitespace creates a large diff even if the data is identical. For reviewing JSON changes in git, consider normalising JSON files with a formatter like jq as part of your commit process (a pre-commit hook or prettier with JSON support). That way git diffs always compare consistently-formatted files.
How do I know if a JSON diff tool is structural or text-based?
Test it: create two objects with the same data but different key order — {"a":1,"b":2} and {"b":2,"a":1} — and paste them in. If the tool reports differences, it’s text-based. If it reports zero differences, it’s structural.
Can I fix false positives in CI without changing my diff tool?
Yes. Add a normalisation step before the diff: jq --sort-keys . file.json > file-normalized.json. Do this for both files, then diff the normalised versions. The overhead is minimal and you can use any diff tool downstream.
My JSON diff shows numeric values as changed even though they’re the same number. What’s happening?
You’re likely comparing 1.0 against 1, or 1e3 against 1000. Text-based diff tools see different strings. A proper JSON diff tool compares the parsed numeric values, which are equal. Switch to a structural diff tool, or normalise with jq which formats all numbers consistently.
Does property order matter in JSON?
No — the JSON specification (RFC 8259) explicitly states that object key ordering is not significant. Any tool or test that treats key reordering as a meaningful change is incorrect. If your API consumers are sensitive to key ordering, that’s a bug in the consumer, not something your API needs to preserve.
If you’re regularly comparing JSON — API responses, config files, test fixtures — the single most effective change you can make is switching from a text diff to a structural one. It eliminates an entire category of noise and lets you focus on what actually changed.
Related reading: How to Compare Two JSON Files Online covers the full range of comparison methods, and How to Compare JSON API Responses During Development goes into the API debugging workflow specifically.