You have two versions of a JSON file: an API response before and after a deploy, two config files, or a translation file from two branches. You want to know what changed. Pasting both into a text diff seems obvious, but it usually shows far more “changes” than there really are.
Why a plain diff of JSON misleads
JSON has two kinds of differences that don’t change the data at all:
- Key order.
{"a":1,"b":2}and{"b":2,"a":1}are the same object. Serializers, databases and languages reorder keys freely, so two equal objects can look completely different line by line. - Formatting. Minified JSON is one long line; pretty-printed JSON spreads the same data over hundreds. Indentation (2 spaces, 4 spaces, tabs) and spacing differ between tools.
A line diff treats both as changes. Compare a minified response with a pretty-printed one and every line differs, which hides the one value you were looking for.
The fix: normalize first, then diff
Make both sides look the same wherever the data is the same, then compare:
- Pretty-print both with the same indentation, so each key and value sits on its own line.
- Sort keys alphabetically, recursively, in every object. Equal objects now have identical lines.
- Keep array order. Arrays are ordered in JSON:
[1,2]and[2,1]really are different, so they must not be sorted. - Diff the result line by line, with changed values marked word by word.
The JSON diff does all four with one click on “Format JSON & sort keys”, in your browser:
Open the JSON diffDoing it on the command line
With jq installed, -S sorts keys and the output is pretty-printed, so this gives the same normalized comparison:
diff <(jq -S . old.json) <(jq -S . new.json)
In Python, the same idea:
import json
def normalize(path):
with open(path) as f:
return json.dumps(json.load(f), indent=2, sort_keys=True).splitlines()
import difflib
print("\n".join(difflib.unified_diff(normalize("old.json"), normalize("new.json"), lineterm="")))Reading the result
- A changed value shows as a modified line, with the old and new value highlighted inside it.
- An added key is a green line on the right only; a removed key is a red line on the left only.
- A moved array item shows as removed in one place and added in another, because arrays are ordered.
- Type changes are real changes:
"42"(a string) is not42(a number).
Common problems
One file isn’t valid JSON
Trailing commas, comments and single quotes are allowed in JavaScript but not in JSON, and they stop any JSON-aware comparison. The validator shows the exact line and column:
Open the JSON validatorBig numbers change
IDs like 12345678901234567890 are larger than JavaScript numbers can hold exactly, so many tools quietly round them, and the rounding then shows up as a change, or worse, hides one. toolspace keeps numbers exactly as written.
Duplicate keys
{"id":1,"id":2} is technically allowed, but parsers keep only the last value, so a duplicate can hide data. The formatter and validator warn about them.
Private data
API responses often hold tokens or customer data. Online tools that upload your JSON to a server put that data at risk. Every toolspace tool runs in your browser, and the page is blocked from sending data anywhere.