{ }jsonkitOpen

How to compare two JSON files (JSON diff)

8 min read · updated 2026-07-12

To compare two JSON files, a text diff seems like enough. It isn't. A line diff flags reordered keys as changes, buries real differences in whitespace noise, and treats equal values with different formatting as different. You want a *structural* JSON diff that understands objects, arrays and value types.

This guide explains why plain-text diffs fail for JSON and what a structural diff computes. It then shows how to run one with an online JSON diff tool, git diff on normalized files and jq, and how to write the result as JSON Patch or JSON Merge Patch. Every method here uses client-side or command-line tools, so your data stays on your machine.

Why a plain-text diff is wrong for JSON

diff, git diff and most editor comparison views work on lines of text. They know nothing about JSON structure. To them a file is just characters split on newlines. That causes three recurring problems.

Key order is insignificant in JSON, but not to a text diff

The JSON specification defines an object as an *unordered* collection of name/value pairs. These two objects hold the same data:

Same data, different key order. A text diff flags every linejsonc
// file A
{ "name": "Ada", "role": "admin", "active": true }

// file B
{ "active": true, "name": "Ada", "role": "admin" }

A structural comparison reports *no differences* here. A line diff reports the whole object as changed because the lines don't match up. Serializers, ORMs and language Map/dict types emit keys in different orders all the time, so this happens often.

Formatting and whitespace create phantom changes

Indent width (2 or 4 spaces), \n or \r\n line endings, and minified or beautified output all change the text without changing the data. If you run one file through a JSON formatter and leave the other minified, a text diff shows almost every line as different, even though the data is identical.

Equal values look different

1.0, 1 and 1e0 are the same JSON number. The strings "a" and "a" are the same JSON string, since both decode to the character *a*. A text diff sees different characters. A value-aware diff parses each side and sees equal values.

What a structural JSON diff computes

A structural diff parses both documents into trees and walks them side by side. It compares objects by key and arrays by index. Instead of changed lines, it gives you a list of differences, each tied to a path and put in one of three categories.

CategoryMeaningExample
AddedA key or element exists in the right document but not the left/user/phone appears in B
RemovedA key or element exists in the left but not the right/user/fax gone in B
ChangedA key exists in both, but the value differs/user/role: "editor" → "admin"

Paths are usually written as JSON Pointer (RFC 6901), such as /items/0/price. The diff is keyed on structure, so reordering an object's keys or reformatting the file produces no differences. The output only contains changes to the data.

Compare JSON files with an online JSON diff tool

A JSON diff tool is the fastest way to see what changed. Paste the two documents. The JsonKit JSON Diff parses both, sorts object keys on each side, and shows a line diff of the normalized output with added and removed lines marked. An option ignores array order. You don't need to sort or reformat anything by hand.

JSON Diff / CompareOpen the tool→

It runs in your browser. Nothing is uploaded. That matters for API responses, config files and fixtures that may contain tokens, internal IDs or customer data. Use it when you want a quick answer to "what changed?" without writing a script.

Compare JSON with git diff on normalized files

When JSON lives in a repository, you often want the diff as a reviewable change. *Normalize before you diff* so git's line-based engine only sees real differences. Normalizing means sorting object keys and using consistent indentation.

Normalize both files, then diff thembash
# Sort keys and pretty-print with 2-space indent (requires jq)
jq -S . old.json > old.norm.json
jq -S . new.json > new.norm.json

# Now a line diff reflects real structural changes
git diff --no-index old.norm.json new.norm.json

The -S / --sort-keys flag makes this reliable. jq sorts every object's keys recursively, so reordered inputs produce identical output. For files in a repo, a git textconv diff driver can pipe JSON through jq -S . before diffing, so git diff always compares normalized text.

Compare and query JSON with jq

jq handles most command-line JSON work. Besides normalizing, it can answer specific comparison questions. Say you only care whether one field changed:

Compare a single field, then diff top-level key setsbash
# Are the "version" values equal?
test "$(jq -r .version old.json)" = "$(jq -r .version new.json)" \
  && echo "same" || echo "changed"

# Show which top-level keys were added or removed
jq -n --slurpfile a old.json --slurpfile b new.json '
  ($a[0] | keys) as $ak | ($b[0] | keys) as $bk
  | { added: ($bk - $ak), removed: ($ak - $bk) }'

The second command loads both files, takes their top-level key sets, and uses jq's array subtraction to find additions and removals. That is a minimal structural diff of the object's shape. For path-level comparisons across large documents, use a dedicated diff tool instead of writing recursion in jq. If you write a lot of these expressions, the JSONPath / JQ tester lets you try queries interactively.

When to ignore array order

Objects are unordered, but JSON arrays *are* ordered. Position is part of the value. By default a diff should respect that and compare element 0 to element 0, element 1 to element 1, and so on. Some arrays represent an unordered set, though, and there the order is a detail you want to ignore.

Order matters

  • A list of steps, events, or timeline entries where sequence is meaningful
  • Paginated or ranked results where position encodes rank
  • Coordinates or tuples like [lng, lat] where index has meaning

Order is incidental

  • A set of tags, roles, or permission strings
  • A collection of objects each identified by a stable id, returned in arbitrary order
  • Enum-like value lists where duplicates and position don't matter

In those cases, an index-by-index diff reports many false changes when the same elements just moved. Sort both arrays by a stable key before comparing, or match elements by an id field instead of by position. Some diff tools let you set an array-matching key so [{id:1},{id:2}] and [{id:2},{id:1}] compare as equal.

Sort an array of objects by id before comparing (jq)bash
jq -S '.items |= sort_by(.id)' old.json > old.norm.json
jq -S '.items |= sort_by(.id)' new.json > new.norm.json
git diff --no-index old.norm.json new.norm.json

Write a diff as JSON Patch or JSON Merge Patch

After you compute a difference, you often need to *send or apply* it. For example, you might send only the changes in a PATCH request or store a compact changelog. Two IETF standards cover this.

JSON Patch (RFC 6902)

JSON Patch is an ordered array of operations (add, remove, replace, move, copy, test). Each one targets a JSON Pointer path. It maps closely to the added/removed/changed model of a structural diff.

A JSON Patch document describing three changesjson
[
  { "op": "replace", "path": "/user/role", "value": "admin" },
  { "op": "remove",  "path": "/user/fax" },
  { "op": "add",     "path": "/user/phone", "value": "+1-555-0100" }
]

JSON Merge Patch (RFC 7386)

JSON Merge Patch is simpler. It looks like the target document. Keys that are present get set, and a null value means "delete this key." It can't edit single array elements or tell "set to null" apart from "delete," but it is compact for updates to objects.

The same changes as a Merge Patch. null means deletejson
{
  "user": {
    "role": "admin",
    "fax": null,
    "phone": "+1-555-0100"
  }
}
AspectJSON Patch (6902)JSON Merge Patch (7386)
ShapeArray of operationsLooks like the document
Array editsYes (by index)No (arrays replaced wholesale)
Delete a keyremove opSet value to null
Set a value to nullreplace with nullNot possible
Best forPrecise, scriptable changesSimple object merges / PATCH bodies

Use JSON Patch when you need precise control, array edits or an audit trail of operations. Use Merge Patch when you send a partial object update to an API and null-means-delete is acceptable.

Frequently asked questions

Why does my JSON diff show changes when the data is the same?

You probably used a text diff, and the two files differ only in key order, indentation or whitespace. Normalize both files first by sorting keys and using the same formatting (for example jq -S .). Or use a structural JSON diff tool that compares by key and value instead of by line.

How do I compare two JSON files on the command line?

Normalize both with jq -S . file.json to sort keys and pretty-print, then run git diff --no-index old.norm.json new.norm.json. jq can also compare specific fields, or find added and removed keys with array subtraction.

Should a JSON diff ignore array order?

Not by default. JSON arrays are ordered, so position is part of the value. Only ignore order for arrays that represent an unordered set, such as tags or collections keyed by id. For those, sort by a stable key or match elements by id before comparing.

What's the difference between JSON Patch and JSON Merge Patch?

JSON Patch (RFC 6902) is an array of explicit operations (add, remove, replace, move, copy, test) that target JSON Pointer paths, and it can edit array elements. JSON Merge Patch (RFC 7386) has the same shape as the document, and a null value deletes a key. It is simpler, but it can't edit array elements or set a value to null.

Is it safe to compare sensitive JSON in an online tool?

Only if the tool runs in your browser and doesn't upload your data. A client-side diff processes everything locally, so tokens, IDs and customer data stay on your machine. If you are unsure, use local tools like jq and git for confidential payloads.

How do I represent the difference between two JSON documents programmatically?

Compute a structural diff and write it as a JSON Patch document, an array of operations keyed on JSON Pointer paths. You can store it, review it and apply it to the original to produce the target document. For simple object updates, a JSON Merge Patch is more compact.