JSON Diff
Compare two documents by structure, so reformatting and key order are invisible.
Nothing you paste leaves your browser. The connect-src allowlist makes that a browser guarantee rather than a promise. Check it yourself
This compares two documents by structure rather than by line. Reformat one of them, sort its keys, change its indentation: the diff stays empty, because none of that changed the data.
A text diff on JSON is close to useless for exactly that reason. Reserialise a document with a different library and every line reports as changed while nothing actually did.
What counts as a difference
Four kinds of change, each reported with a JSON Pointer to the exact location.
- Added
- A key or element present on the right and not on the left.
- Removed
- Present on the left and not on the right.
- Changed
- Present in both with a different value of the same type.
- Retyped
- Present in both with a different type. Worth separating, because a number that became a string is usually a serialisation bug rather than a data change.
How arrays are compared, and why it matters
Arrays are the hard part of any structural diff, and there is no single right answer.
Comparing position by position is correct for a fixed-shape tuple and catastrophic for a list. Insert one element at the front of a thousand-element array and every position after it reports as changed, which buries the one real difference under a thousand false ones.
This tool runs a longest-common-subsequence pass instead, so an insertion is reported as an insertion. That costs time proportional to the product of the two lengths, so above a threshold it falls back to positional comparison and says in the notes that it did. It never silently degrades.
Things that are deliberately not differences
- Key order
- JSON objects are unordered by specification. Two documents with the same members in a different order are the same document.
- Whitespace and indentation
- Insignificant to a parser, so invisible here.
- 0 and -0
- Distinct under Object.is but identical as JSON numbers.
How to do this in code
Structural comparison in code.
sh jq
The first line is the one to remember. Sorting keys first removes most of the noise from a text diff.
# Sort keys recursively, then diff the text. Cheap and
# surprisingly effective for small documents.
diff <(jq -S . a.json) <(jq -S . b.json)
# Keys present in one and not the other
jq -n --slurpfile a a.json --slurpfile b b.json \
'($a[0] | paths) - ($b[0] | paths)' py Python
from deepdiff import DeepDiff
diff = DeepDiff(a, b, ignore_order=True)
print(diff)
# Standard library only, for a shallow comparison
changed = {k for k in a.keys() | b.keys() if a.get(k) != b.get(k)} js JavaScript
JSON Patch is the standard wire format for a JSON diff, and the paths are RFC 6901 pointers like the ones this tool reports.
import { compare } from 'fast-json-patch';
// RFC 6902 JSON Patch: a list of operations that turns a into b
const patch = compare(a, b);
// [{ op: 'replace', path: '/user/name', value: 'Priya' }] go Go
go-cmp is the standard choice in Go tests and produces readable output for nested structures.
import "github.com/google/go-cmp/cmp"
if d := cmp.Diff(a, b); d != "" {
t.Errorf("mismatch (-want +got):\n%s", d)
} Questions
- Why does reordering keys show no difference?
- Because RFC 8259 defines an object as an unordered collection. Every parser in practice preserves insertion order, and diffs that depend on it are testing serialisation rather than data.
- Can I ignore fields like timestamps?
- Yes. Ignoring a path removes it from the comparison entirely, which is how you compare two API responses that differ only in requestId and generatedAt.
- What is a JSON Pointer?
- RFC 6901: a slash-separated path such as /users/0/email. Two characters need escaping inside a key: ~ becomes ~0 and / becomes ~1. Every change reported here carries one, so you can address the location programmatically.