Compare two JSON arrays by id

Compare two lists of JSON objects by id and see which items changed, moved, were inserted or were deleted.

API responses, database exports and config files often hold a list of objects that each carry an id, and the same list can come back in a different order. A plain diff reads that as a wall of changes, because position 0 is compared with position 0. This page opens with Match array items by key switched on, so each object is paired with the object that has the same id, and the list shows only what really differs: a field that changed, such as /users[id=1]/role from admin to owner, an item that was deleted, an item that was inserted, and an item that moved to another place. It looks for id, _id, key and uuid on every item, or you can type a key such as sku. The sample shows all four kinds in a three user list. One honest limit: ids must be unique within a list and present on every object, otherwise that list is compared by position and the page tells you so.

How it works

  1. Paste the original JSON and the changed JSON
  2. Choose whether array order and letter case matter
  3. Read the change list or copy the JSON Patch

How the JSON diff works

A JSON diff compares the meaning of two JSON texts, not their lines, and lists every value that was added, removed or changed at its JSON Pointer path, such as /owner/id (lists matched by key use a readable identity path instead, described below). Both texts are parsed first, so spacing, line breaks and the order of keys in an object never count as a change. Objects are compared key by key, lists item by item, and everything else value by value. A value with a different type, such as the number 7 against the string "7", is reported on its own as a type change, because code that reads it will behave differently. The number 1 and the number 1.0 are the same value in JSON, so they are never reported. Each line of the change list starts with a sign: + for added, - for removed, ~ for changed.

A worked example

Take {"version": 1, "tags": ["a", "b"], "owner": {"id": 7}, "legacy": true} and change it to {"version": 2, "tags": ["a", "b", "c"], "owner": {"id": "7"}, "beta": true}. The page lists five differences. /version changed from 1 to 2. /tags/2 was added with the value "c". /owner/id changed type from number to string. /legacy was removed and /beta was added. The page opens with a longer sample of the same kind, so you can see this result before you paste anything.

Array order, letter case and matching by key

By default list items are compared by position, so moving one item to the front of a ten item list changes up to ten positions. Turn on Ignore array order to match items wherever they sit: plain values match as a multiset, so two copies of 3 need two copies on the other side, and objects match when every key and value is equal. Turn on Ignore letter case in strings to treat "Yes" and "yes" as equal; keys are always compared exactly. With array order ignored, an object that changed inside a list shows as one removal and one addition, because the page does not guess which object became which. Turn on Match array items by key to compare lists of objects by identity instead of position. The page uses the first of id, _id, key and uuid that every item in both lists has, as a string or number and never repeated, or the key you type, such as sku. Items are then reported as changed with identity paths like /users[id=1]/role (these name the item by its key and are not JSON Pointers; the patch uses real pointers such as /users/0/role), deleted, inserted, or moved when their place among the other matched items changed, not when a delete or insert only shifted their index. A list with no usable key is compared by position and the page says so.

The JSON Patch output

The second output is a JSON Patch as defined in RFC 6902: a list of add, remove and replace operations that turns the original into the changed JSON when they are applied in order. Removals inside a list run from the last index to the first so earlier positions stay valid. With Ignore letter case on, a change of case is not listed as a difference, but the patch still includes it, so applying the patch gives the changed JSON exactly. With Ignore array order on, the patch makes the lists equal as sets of items and appends new items at the end, so it does not reproduce the order of the changed list. Move operations are generated only when Match array items by key is on, where they put matched items into the order of the changed list; otherwise a moved value shows as a removal and an addition.

Errors and limits

Each box is checked on its own and a problem is reported as Original or Changed, then the line and column, such as Line 3, column 5. The rules are strict JSON: no comments, no trailing commas, double quotes only. Numbers are read as JavaScript numbers, so integers beyond 9,007,199,254,740,991 lose precision before they are compared, and a key that appears twice in one object keeps its last value. Each text can be up to 500,000 characters. The list stops at 5,000 differences, and then no patch is made. Everything runs in this tab.

Frequently asked questions

Why is an item moved when only its index changed?

A move is reported when an item's place among the other matched items changed. In [1,2,3] against [2,3] the ids 2 and 3 only shift up one index after 1 is deleted, so just the deletion is listed. Swapping ids 1 and 2 reports one move.

Does the patch handle moved items?

Yes. With a key, the JSON Patch removes deleted items, adds inserted ones at their index and uses RFC 6902 move operations, so applying it to the original gives the changed list in the same order. With Ignore array order on, it only appends new items.

Can I match on a key that is not id?

Yes. Type the name in Key to match on, for example sku, email or slug. The values must be strings or numbers, present on every object and different from each other in both lists, and 1 and "1" count as different values.

How do I compare two JSON files online?

Paste the first file into the Original box and the second into the Changed box. The page lists every difference by path, for example ~ /version: 1 -> 2, and counts additions, removals, changes and type changes. Nothing needs to be uploaded.

Why does the JSON diff ignore the order of keys?

In JSON an object is an unordered set of keys, so {"a":1,"b":2} and {"b":2,"a":1} hold the same data. Key order is ignored on this page and cannot be switched on. List order does matter by default, because a list is ordered.

How do I ignore the order of items in an array?

Turn on Ignore array order. Then [1, 2, 3] and [3, 1, 2] are equal, and [1, 1, 2] differs from [1, 2] by one extra 1. Objects in a list match when all of their keys and values are equal.

How do I compare two JSON arrays of objects by id?

Turn on Match array items by key. Items with the same id are paired wherever they sit, so [{"id":1},{"id":2}] against [{"id":2},{"id":1}] reports one move, and a changed role shows as ~ /users[id=1]/role. Without it, position 0 is compared with position 0.

What happens when my objects have no id?

If the key is not on every item, is repeated, or is not a string or number, that list is compared by position and a note says how many lists were. Type another key, such as sku or email, in Key to match on, to use it instead of id, _id, key and uuid.

What is the JSON Patch output for?

It is the same change set in the RFC 6902 format, such as {"op": "replace", "path": "/version", "value": 2}. Libraries in most languages can apply it to the original document to produce the changed one. Paths use ~0 for ~ and ~1 for / inside a key.

Why is 7 against "7" a type change and not a plain change?

One is a number and one is a string, and code that checks the type treats them differently. The page counts those separately under Type changed so you can find API changes that keep the same digits.

Is my JSON uploaded?

No. The text is processed in your browser and not uploaded. Your two JSON texts are never saved; only your option settings are kept on this device.