guide/json-diff.md

JSON を比較する方法 — キー順のソートと正規化

2つの JSON を比較するとき、キーの順序・インデント・数値表記の違いで生じる見かけだけの差分をなくす方法と、配列の順序、大きな整数、JSON Patch との違いを説明します。

最終更新: 2026-09-23

API のレスポンス、設定ファイル、翻訳ファイルなど、JSON 形式のデータを比較する機会はよくあります。ところが2つの JSON をそのままテキスト比較すると、データは同じなのに差分が大量に出ることが少なくありません。この記事では、その理由と解決方法を説明します。

なぜ見かけだけの差分が出るのか

JSON の標準(RFC 8259)は、オブジェクトを「順序のない名前/値のペアの集まり」と定義しています。つまり {"a":1,"b":2}{"b":2,"a":1} は同じデータを表します。しかしテキストとしては行の順序が違うため、diff は差分とみなします。ほかにも、データとは関係なく次のような違いが生じます。

解決策:パースしてから同じ形で書き直す

最も確実な方法は、2つの JSON をパースしてデータにしてから、同じ規則で再びシリアライズすることです。

  1. 2つのテキストを JSON としてパースします。ここで文法エラーがあれば先に直します。
  2. すべてのオブジェクトのキーを再帰的にソートします。
  3. 同じインデント(例:2スペース)で書き直します。
  4. その結果を行単位で比較します。

たとえば次の2つの JSON は、キーの順序と配列の順序が異なります。

{"name":"kim","age":30,"tags":["a","b"]}
{"age":30,"name":"kim","tags":["b","a"]}

ソートして書き直すと、キー順の違いは消え、本当の違いである配列の順序だけが残ります。

@@ -2,7 +2,7 @@   "age": 30,   "name": "kim",   "tags": [-    "a",-    "b"+    "b",+    "a"   ] }

配列の順序はソートしない

オブジェクトと違い、配列は順序に意味があります。 ["a","b"]["b","a"] は別のデータです。そのため、正規化の際に配列はソートしないのが原則です。ただし、タグの一覧のように業務上順序が重要でない配列なら、比較の前に自分でソートしておくと結果が読みやすくなります。どちらが正しいかはデータの意味しだいです。

パースによって変わってしまうもの

パースしてから書き直す方法には、知っておきたい副作用があります。

コマンドラインで行う方法

jq がインストールされていれば、-S--sort-keys)オプションでキーをソートして出力できます。

jq -S . before.json > a.json
jq -S . after.json  > b.json
diff -u a.json b.json

JSON Patch との違い

diff の結果は、人が読むための行単位の変更リストです。一方、JSON Patch(RFC 6902)は {"op":"replace","path":"/age","value":31} のように JSON 構造のパスで変更を表す標準で、JSON Merge Patch(RFC 7396)は変更部分だけを含む JSON を元のデータに上書きする方式です。プログラムどうしで変更をやり取りするならこれらの標準が、人がレビューするならソート後のテキスト diff が適しています。

このツールで試す

テキスト比較ツールのオプションで JSON キーをソートをオンにすると、両方をパースしてキーをソートし、2スペースのインデントで書き直してから比較します。配列の順序は変えず、パースに失敗した場合はどちら側の何文字目でエラーが起きたかを知らせます。.json ファイルを2つまとめてドロップしてもかまいません。結果を patch として読む方法は unified diff の読み方を参照してください。

テキスト比較ツールを使う