How to Compare Two JSON Files and Spot the Differences That Matter
Why a text diff misleads on JSON, which changes are real, and the subtle differences such as types, null and array order.
Why a plain text diff misleads
Two JSON files can hold identical data and still look completely different as text. Object keys can appear in a different order, and one file can be minified while the other is indented. A line-by-line diff flags every line, which hides the changes that matter.
A structural comparison parses both files first, then compares the data itself. Key order and whitespace stop mattering, and only real differences are left.
The three kinds of change
- Added: a key or array item exists in the second file but not the first.
- Removed: it exists in the first file but not the second.
- Changed: the key exists in both but the value is different.
For nested data, a good report shows the full path, such as user.address.city, so you can find the change without scanning.
Worked example: reading a diff
Take two versions of a user record. The first file is:
{"id":7,"name":"Asha","active":true,"tags":["a","b"],"address":{"city":"Pune","zip":"411001"},"plan":"free"}
The second file is:
{"name":"Asha","id":7,"active":"true","tags":["b","a"],"address":{"city":"Pune","zip":"411001","country":"IN"},"plan":null}
A text diff shows almost every line as different. A structural comparison reports four real changes:
- active: changed from the boolean true to the string "true". This is a type change.
- tags: the array order changed, so tags[0] went from "a" to "b" and tags[1] from "b" to "a".
- address.country: added, with the value "IN".
- plan: changed from "free" to null.
The order of id and name is not reported, because object key order carries no meaning. Of the four changes, the type change is the one most likely to break code that checks active with a strict comparison.
Scenario: a config that drifted between environments
A team notices that a feature works in staging but not in production. Both environments load a settings file, and both files are several hundred lines long, so reading them by eye is slow and error prone.
They paste each file into a structural comparison. The report lists three differences: a timeout that is 30 in staging and 5 in production, a feature flag that exists only in staging, and a database host that differs, which is expected. The missing flag explains the bug.
The fix takes one line, and the comparison also shows that nothing else changed. The same check run before every release would have caught the drift earlier, which is the main value of comparing structure rather than text.
Report format for each change
| Change | Path | Before | After |
|---|---|---|---|
| Changed | active | true | "true" |
| Changed | tags[0] | "a" | "b" |
| Changed | tags[1] | "b" | "a" |
| Added | address.country | absent | "IN" |
| Changed | plan | "free" | null |
The path uses dots for object keys and square brackets for array positions. That is enough to find any change in a deeply nested file.
Differences that look the same
Some changes are easy to miss and often cause bugs:
- Type changes: the string "1" and the number 1 print alike but behave differently.
- Null versus missing: a key set to null is not the same as a key that is absent, and many APIs treat them differently.
- Number formatting: 1.0 and 1 are equal in value, while "1.0" is a different string.
- Array order: object keys are unordered, but array order is meaningful, so a reordered array is a real change.
When to compare
- Before and after a deploy, to confirm an API response changed only as intended.
- Between environments, to find configuration drift between staging and production.
- After a migration, to check that no field was dropped.
- When reviewing generated files, such as lock files or exported settings.
The JSON Difference Checker runs in your browser, so the data you paste is not uploaded. If a file will not load, validate it first with the JSON Validator to find the syntax error.
Troubleshooting a noisy comparison
- Too many differences in an array of objects: the items are probably in a different order. Sort both arrays by a stable key such as id before comparing, or accept that a reorder counts as a change.
- Every number differs: one export may format numbers as strings. Check the types before assuming the values are wrong.
- Timestamps and generated ids always differ: remove those fields from both sides first so real changes are not buried.
- The file will not load: a trailing comma, single quotes or a comment is not valid JSON. Run the JSON Validator on it to find the line.
- Large numbers differ in the last digits: some tools parse numbers as floating point, which cannot hold integers above 9007199254740991 exactly. Compare such ids as strings.
Frequently asked questions
- Does key order matter in JSON? Not for objects, so two objects with the same keys and values in a different order are equal. It matters for arrays, where order is part of the data.
- Is null the same as a missing key? No. A null value says the field exists and is empty, while a missing key says nothing about it. Many APIs and databases treat the two differently.
- Can I compare a minified file with a formatted one? Yes. Whitespace outside strings is ignored when the file is parsed, so only the data is compared.
- How do I compare very large JSON files? Parse both files and compare the data. If the file is too big for the browser tab, split it or compare only the sections you need.
- Why does 1 differ from 1.0 in some tools? Most parsers read both as the same number, but a tool that compares the original text sees two different strings. A structural comparison treats them as equal numbers.