Online JSON Diff Tool: Compare and Analyze JSON Data Differences
What Is JSON Diff
JSON Diff compares two JSON data structures and identifies differences. It detects additions, deletions, and modifications between two JSON objects. Drop two payloads into our JSON Diff tool to highlight the changes side by side.
Comparison Example
Original JSON:
{
"name": "Alice",
"age": 28,
"hobbies": ["reading", "gaming"]
}
Modified JSON:
{
"name": "Alice",
"age": 29,
"hobbies": ["reading", "coding"],
"email": "alice@example.com"
}
Diff Result:
{
"age": { "old": 28, "new": 29 },
"hobbies[1]": { "old": "gaming", "new": "coding" },
"email": { "added": "alice@example.com" }
}
Why You Need JSON Diff
- API response validation: Compare API returns with expected values (the online JSON diff pastes-and-compares with four granularity levels)
- Config change detection: Compare configuration file modifications
- Data migration verification: Confirm data migration correctness
- Code review: Compare JSON output between two versions
- Automated testing: Assert data changes in tests
- Version control: Track JSON data change history
Diff Result Types
| Change Type | Description | Marker |
|---|---|---|
| Added | Key doesn't exist in original | + or added |
| Deleted | Key exists in original but not modified | - or deleted |
| Changed | Key exists but values differ | ~ or changed |
| Unchanged | Key and value are the same | No marker |
How to Use an Online Tool
Using ToolVault's JSON Diff Tool:
- Enter original JSON on the left
- Enter modified JSON on the right
- The tool auto-compares and highlights differences
- Shows specific additions, deletions, and changes
- Copy the diff list, or copy an RFC 6902 JSON Patch with one click
Noise-free comparison: ignore array order and ignore keys
When diffing API responses or logs, the fields you actually care about get buried under two kinds of noise: order-insignificant arrays (tags, permission lists) and metadata fields that change on every write (updatedAt, traceId). The tool panel offers three options:
- Ignore array order: arrays match as multisets —
[1,2,3]and[3,1,2]are identical; leftover elements pair up by minimal difference, reported as modifications instead of "whole row removed + whole row added". - Array key (e.g.
id): combined with ignore-order, object arrays align on a business key. Rows with the sameidreport field-level diffs likeusers[id=7].nameeven when they moved position. - Ignore keys: comma-separated field names applied at any depth, e.g.
updatedAt, traceId— skipped entirely, leaving only signal in the diff list.
The judgement call is unchanged: do not enable "ignore array order" for order-sensitive APIs (timelines, leaderboards). When in doubt, run the default positional mode first.
Exporting JSON Patch (RFC 6902)
A diff list is for humans; machines (CI assertions, change replay) consume JSON Patch. Click "Copy JSON Patch" to get a standard sequence of add / remove / replace operations with JSON Pointer paths (e.g. /config/timeout). Two boundaries to know:
- With "ignore array order" enabled, array paths are logical positions, not indices — they cannot map to executable patches, so the patch button hides in that mode. Use the diff list instead.
- Big integers (like snowflake IDs) keep their exact text form inside the patch output; but any implementation that consumes the patch via
JSON.parsestill limits numbers to IEEE 754 doubles — that is a boundary of the patch protocol itself, not of this tool.
JSON Diff in Code
JavaScript
// Deep compare two objects
function deepDiff(oldObj, newObj, path = '') {
const diffs = {};
const allKeys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
for (const key of allKeys) {
const currentPath = path ? `${path}.${key}` : key;
if (!(key in oldObj)) {
diffs[currentPath] = { type: 'added', value: newObj[key] };
} else if (!(key in newObj)) {
diffs[currentPath] = { type: 'deleted', value: oldObj[key] };
} else if (typeof oldObj[key] === 'object' && typeof newObj[key] === 'object') {
Object.assign(diffs, deepDiff(oldObj[key], newObj[key], currentPath));
} else if (oldObj[key] !== newObj[key]) {
diffs[currentPath] = { type: 'changed', old: oldObj[key], new: newObj[key] };
}
}
return diffs;
}
Python
def deep_diff(old, new, path=''):
diffs = {}
all_keys = set(list(old.keys()) + list(new.keys()))
for key in all_keys:
current_path = f"{path}.{key}" if path else key
if key not in old:
diffs[current_path] = {'type': 'added', 'value': new[key]}
elif key not in new:
diffs[current_path] = {'type': 'deleted', 'value': old[key]}
elif isinstance(old[key], dict) and isinstance(new[key], dict):
diffs.update(deep_diff(old[key], new[key], current_path))
elif old[key] != new[key]:
diffs[current_path] = {'type': 'changed', 'old': old[key], 'new': new[key]}
return diffs
Structural diff vs textual diff: when line diff lies about JSON
A one-line JSON (minified) has no lines for a line-diff to work with — it reports "everything changed". A pretty-printed reordering (same object, keys alphabetized by a different serializer) produces a thousand-line textual diff with zero semantic change. The fix is structural: parse both sides, compare trees, report paths — order-insensitive for object keys (per JSON spec), order-sensitive for arrays (per semantics). Our JSON diff does the structural pass; the discipline it encodes is that textual diff is the wrong tool for machine-generated JSON. Reserve line-diff for hand-edited files where line context has meaning.
Array diffing: the alignment problem
Arrays are diffed positionally by naive tools: element 5 deleted shifts every subsequent element, reported as N changes when one insertion occurred. Smarter array diffs (LCS-based, like the Myers algorithm text diff applied to serialized elements) align by content and report the single insertion. The trade: LCS on large arrays of objects is expensive, and "similar but not equal" elements (same id, changed fields) defeat naive alignment — the production-grade answer keys arrays by id-field when the schema has one, treating the array as a map for diff purposes. If your diff tool shows massive array churn on small changes, it lacks key-based alignment; check with an id-keyed sample.
Normalizing before diffing: the etiquette of stable comparisons
Machine-written JSON varies by serializer: key order, number formatting (1.0 vs 1), unicode escapes (\u0041 vs A), insignificant whitespace. Normalization — canonical serialization (sorted keys, standardized number format, no whitespace) before diffing — removes this entire noise class. RFC 8785 (JSON Canonicalization Scheme) is the formal spec; a pragmatic subset (sort keys, normalize numbers) captures 95% of the benefit. Diff tools that normalize first report signal; tools that diff raw bytes report serializer quirks. When diffs feed automated pipelines (release notes, change detection), normalization is not optional — the pipeline fires on every serializer upgrade otherwise.
FAQ
Can JSON Diff Handle Large Files?
The tool runs a full recursive comparison locally in the browser: configs and responses of a few MB compare in seconds. For files beyond ~10MB, first trim irrelevant subtrees with "ignore keys", or extract the paths you care about with jq before comparing. The leftover-element pairing in "ignore array order" mode is an O(n²) greedy pass — unordered arrays with thousands of elements will feel slow; filling in the "array key" when elements have a business key cuts that cost substantially.
Can Diff Results Be Merged?
Yes. Click "Copy JSON Patch" to get an RFC 6902 patch (add / remove / replace) that libraries like fast-json-patch can apply back onto the original JSON. Note that in "ignore array order" mode the paths carry no index semantics, so patch export is unavailable there.
How to Handle Array Order Changes?
Array diffing has two strategies: positional comparison (default — strict but order-sensitive) and content-based comparison (enable "ignore array order" — order-insensitive but positions are lost). When elements are objects with an id, also fill in the "array key" to align on the business key, giving you both order-insensitivity and field-level precision.
Implementation notes
Building this tool hit a big-integer pitfall: parsing must run in a lossless mode (raw numeric text preserved), and equal-value big integers must compare by that raw text — compare by instance reference of the parsed output instead, and two large IDs with identical values get reported as modified, a pure phantom diff.
From the ToolVault developer notebook. Browse more online tools on the homepage.
Related Tools
Related Articles
Online CSV to SQL Tool: Import Tabular Data into Databases
CSV to INSERT with empty cells → NULL, MySQL backticks or PostgreSQL quotes, BOM stripping, Markdown pipe escaping, and when to switch to COPY/LOAD instead.
HTML Table to JSON: How to Preserve Rows, Columns and Structure
Converting an HTML table from a web page to JSON — how to keep headers, handle merged cells and nested structure? Step-by-step with our HTML table to JSON tool.
Excel to JSON: Dates Become Numbers / Chinese Garbled — Fix
When converting Excel to JSON, dates turn into numbers like 44927, Chinese is garbled, empty cells vanish? Explains Excel serial dates and encoding pitfalls, with our Excel to JSON tool.