How to compare two JSON files semantically (not just as text)
A text diff treats reordered keys as changed. A JSON diff understands structure. Use the right tool to spot the actual differences.
Comparing two JSON documents seems like a job for a text diff. Just run
diff a.json b.json and read the result. The problem with that
approach: a text diff treats JSON as character sequences. Reorder the keys in an
object and a text diff says "this changed". Add or remove whitespace and a text
diff says "this changed". Both are textually different but semantically identical
(JSON objects are unordered; whitespace is insignificant). For API response
debugging, configuration comparison, or any task where "what actually changed"
matters, a JSON-aware diff is the right tool.
Text diff vs JSON diff
Consider these two documents:
// a.json
{ "name": "Alice", "age": 30, "city": "NYC" }
// b.json
{
"city": "NYC",
"age": 30,
"name": "Alice"
} A text diff reports every line as different. A JSON diff reports zero changes — both documents represent the same object. JSON objects don't have ordered keys (per RFC 8259), and most JSON consumers (parsers, serializers, schema validators) treat them as unordered. The text-diff result is technically correct but practically useless; the JSON-diff result is what you actually want.
Conversely, consider:
// a.json
{ "items": [1, 2, 3] }
// b.json
{ "items": [1, 3, 2] }
A JSON diff reports two changes: items[1] changed from 2 to 3, and
items[2] changed from 3 to 2. Arrays in JSON are ordered
(per the spec), so reordering an array is a semantic change. This matches
every real JSON consumer's interpretation.
Step-by-step with our tool
- Open justfiletools.com/tools/json-diff.
- Paste the first JSON document into the left textarea.
- Paste the second JSON document into the right textarea.
- Click Compare. The output shows every path that differs, with the old and new values.
Each change is reported as a single line: the path (using JavaScript dot-and-bracket
notation, like users[0].email), the change type (added, removed,
changed), and the values involved. The change types map to:
- added — the path exists in the new version but not the old. Shows the new value.
- removed — the path exists in the old version but not the new. Shows the old value.
- changed — the path exists in both but the value is different. Shows old → new.
The path notation
Paths use the syntax you'd type in JavaScript to reach a value:
server.host— thehostproperty of theserverobject.users[0].email— theemailproperty of the first user in theusersarray.[3].name— thenameproperty of the fourth item in a top-level array.["user-id"]— a property name that contains characters not allowed as an identifier (dashes, spaces).(root)— when the entire document changed type (object replaced by array, etc.).
Useful workflows
API response debugging. Save the response from your test environment and the response from production for the same endpoint. Paste both into the JSON Diff. The output shows exactly which fields differ, which is faster than reading both documents and trying to spot changes visually.
Configuration drift detection. Compare a Kubernetes manifest, a docker-compose file, or any structured config across environments. The diff surfaces the actual differences (port numbers, resource limits, image tags) without drowning in formatting noise.
Schema validation. Compare a JSON Schema to a sample document to spot fields you forgot to define, or to identify where the schema and the sample have drifted apart.
Database row comparison. Dump two rows of jsonb data
from PostgreSQL and compare. The diff shows which fields changed in a record over
time.
Common pitfalls
Type mismatches are reported as single changes. If one side has
"count": 42 and the other has "count": "42", the diff
reports a single "changed: number → string" entry rather than separate add/remove
operations. This is the more useful interpretation.
null vs missing. { "a": null } and {} are
different. The first explicitly states that a is null; the second says
nothing about a. The diff reports { "a": null } →
{} as a removed key. Most consumer code treats them differently in
practice.
Whitespace and key order are ignored. The diff parses both sides to structured values before comparing. Formatting differences disappear.
Code equivalent
The algorithm is a recursive walk of both trees in parallel. For objects: collect the set union of keys, and for each key recurse on the two sides' values (or mark added/removed if a key is in only one side). For arrays: walk by index, recurse on each position. For primitives: equality check.
Plenty of libraries implement this (jsondiffpatch, deep-diff, microdiff). Our in-browser tool uses a small custom implementation tuned for the kind of output that's useful at a glance — flat list of paths-and-changes, no nested visualization. For more elaborate visual diffs (color-highlighted side-by-side, collapsible nesting), a desktop tool like Beyond Compare or a code editor's JSON diff plugin is the right call.
Alternative approaches
- jq + diff.
diff <(jq -S . a.json) <(jq -S . b.json)normalizes both files (sorted keys, consistent formatting) and runs a text diff on the normalized versions. Works for simple cases but reports formatting differences as changes if the documents have different array element ordering on insignificant arrays. - VS Code's JSON diff. Open both files and use the side-by-side
compare view. Visual, color-highlighted, decent for small files. Treats JSON as
text (same caveat as
diff). - jsondiffpatch. The most popular JS library for semantic JSON diffs, with a JSON Patch (RFC 6902) output mode useful for replicating changes programmatically. Heavyweight for a one-off comparison; right tool for production diff replay.
- Postman / Insomnia. If you're comparing API responses, both have response-diff features when you save responses to history. Convenient for API debugging.
Privacy considerations
JSON files you'd want to diff often contain real data — production API responses
with user emails, configs with secrets, log entries with internal identifiers. The
in-browser tool's diff runs as a JavaScript function in your tab. Both inputs are
parsed with native JSON.parse and walked locally. Verify in DevTools:
zero network requests during the diff.
Related tools and guides
- JSON Diff — the tool this guide covers.
- JSON Formatter — clean up both inputs first.
- String to JSON — unwrap stringified JSON before comparing.
- Text Diff — line-level diff for non-JSON content.
- How to unescape stringified JSON.
Try it now: JSON Diff
Structural diff of two JSON documents — added, removed, and changed paths
Open JSON Diff