induwara.lk
induwara.lkDeveloper · JSON

JSON Diff Checker — compare two JSON documents

Paste two JSON documents and see only the real changes — every added, removed, and changed value by path, ignoring key order and formatting. You also get a copyable RFC 6902 JSON Patch. It runs entirely in your browser, so nothing is uploaded.

By Induwara AshinsanaUpdated Jul 8, 2026
Compare two JSON documents
Paste or type strict JSON.
Paste or type strict JSON.
Actions
Added
Removed
Changed
Status
Paste JSON into both boxes to see a structural, key-aware diff — every added, removed, and changed value with its JSON Pointer path, plus a ready-to-copy JSON Patch. Click Load sample to try it.

Runs entirely in your browser — nothing is uploaded, logged, or stored. Objects are compared key-set-wise (key order ignored, per RFC 8259 §4); numbers compare by value, so 1, 1.0 and 1e0 are equal. Arrays are compared positionally. Strict JSON only (no comments or trailing commas). Maximum 2,000,000 characters per side.

How it works

A line-based diff treats JSON as plain text, so it flags a reordered key or a change in indentation as a difference even when the data is identical. This tool does a structural comparison instead: it parses both documents into values and compares those values, so only genuine semantic changes are reported. The method is deterministic — the same two inputs always produce the same result.

  1. Parse. Both sides are parsed against the strict JSON grammar defined by RFC 8259 and ECMA-404. If either fails, the exact parser error with its line and column is shown and no diff is produced — you never see a misleading partial comparison.
  2. Compare recursively. Two objects are compared by the union of their keys: a key only in A is removed, only in B is added, and in both is compared recursively. Because RFC 8259 §4 defines an object as an unordered collection of name/value pairs, key order is never itself a difference. Two arrays are compared element by element by index (positional); trailing extras in B are added and trailing extras in A are removed. Two primitives are equal only when they share a JSON type and value.
  3. Handle numbers and types. A JSON number denotes a value (RFC 8259 §6), so 1, 1.0, and 1e0 compare equal. A value whose JSON type changes — a number becoming a string, or an object becoming an array — is reported as a type change rather than a silent value change.
  4. Label every change. Each difference carries its JSON Pointer (RFC 6901), built segment by segment, escaping ~ as ~0 and / as ~1 inside keys — for example /user/roles/1.
  5. Emit a patch. Each change maps to an RFC 6902 operation: added → add, removed → remove, changed → replace. Array removals are ordered highest-index-first so the patch applies cleanly in sequence.

To prove the result is correct, the tool cross-checks itself: it applies the patch it just produced back to document A and confirms the outcome equals document B, value for value. The Verified · patch round-trips badge turns green only when that independent second computation succeeds — the same idea as double-entry verification.

Worked examples

Flat object — add, remove, and change

A — original

{"name":"Nimal","age":30,"city":"Colombo"}

B — changed

{"name":"Nimal","age":31,"country":"Sri Lanka"}
  1. /name — unchanged ("Nimal" equals "Nimal")
  2. /age — changed 30 → 31
  3. /city — removed (only in A)
  4. /country — added (only in B)
  5. Counts: added 1, removed 1, changed 1
  6. Patch: [replace /age 31, add /country "Sri Lanka", remove /city]

Nested object and array element

A — original

{"user":{"id":1,"roles":["admin","editor"]},"active":true}

B — changed

{"user":{"id":1,"roles":["admin","viewer"]},"active":true}
  1. Recurse into /user — id equal (1 == 1)
  2. Recurse into /user/roles — index 0 equal ("admin")
  3. /user/roles/1 — changed "editor" → "viewer"
  4. /active — unchanged (true == true)
  5. Counts: added 0, removed 0, changed 1
  6. Patch: [replace /user/roles/1 "viewer"]

Type change, number equivalence, array growth

A — original

{"price":100,"discount":1.0,"tags":["a"]}

B — changed

{"price":"100","discount":1,"tags":["a","b"]}
  1. /price — changed 100 (number) → "100" (string): type change
  2. /discount — unchanged (1.0 and 1 denote the same number)
  3. /tags/0 — unchanged ("a")
  4. /tags/1 — added "b" (array grew from length 1 to 2)
  5. Counts: added 1, removed 0, changed 1
  6. Patch: [replace /price "100", add /tags/1 "b"]

Frequently asked questions

Sources & references

The comparison rules and pointer/patch formats on this page were last cross-checked against these standards on 2026-07-08. Every diff is additionally self-verified at runtime by round-tripping its patch.

Related tools

Rate this tool
Be the first to rate

Comments & feedback

Spotted a bug or want an improvement? Tell us — our team reviews every comment, and good ideas get built. Comments are public and anonymous.

Found a bug, edge case, or want to suggest an improvement?

Email me at [email protected] — most fixes ship within 24 hours.