JSONを比べると差分だらけになる原因|見かけの差分と消える差分
同じデータなのに、2つのJSONを比べると差分だらけになるのはなぜか。キーの順番・空白・文字のエスケープ・数値の書き方・配列の並びの5つを、Python・PHP・JavaScriptで実際に出力した例で整理します。値で比べても消えてしまう差(大きな整数と重複キー)も説明します。
公開読了目安 約5分
この記事の要点
- 同じデータでも、書き出す言語や設定で JSON の字面は変わる
- キーの順番・空白・エスケープ・数値の書き方は、値が同じでも行の差分になる
- 配列の並びは、データによって意味がある場合と無い場合がある
- 大きな整数と重複キーは、値で比べても差が消えることがある
- 字面をそろえてから比べるか、値で比べるかを先に決める
目次
2 つの JSON を行で比べて差分だらけになるのは、多くの場合、中身ではなく書き方が違うからです。 キーの順番・空白・文字のエスケープ・数値の書き方は、値が同じでも別の行になります。
見かけの差分に埋もれると、本当に変わった 1 項目を見落とします。 この記事では、見かけの差分が生まれる 5 つの原因と、逆に消えてしまう差を順に整理します。
同じデータでも、書き出す言語で字面が変わる
まず、同じ 3 項目を 3 つの言語で JSON に書き出した結果を並べます。 どれも既定の設定のまま出力しました。
Python {"name": "佐藤", "score": 80.0, "url": "https://a/b"}
PHP {"name":"佐藤","score":80,"url":"https:\/\/a\/b"}
JavaScript {"name":"佐藤","score":80,"url":"https://a/b"}
3 行とも、名前は「佐藤」、点数は 80、URL は同じ値です。 それでも、行として比べると 3 行すべてが違います。
Python は区切りの後ろに空白を入れ、日本語を \u の形にします。
PHP はスラッシュを \/ にし、80.0 を 80 と書きます。
JavaScript は日本語をそのまま出します。
データを作る側の言語が変わったり、設定が 1 つ変わったりするだけで、ファイルの差分は大きくなります。
見かけの差分を生む 5 つの原因
値が同じなのに行の差分になる原因は、次の 5 つに分けられます。 上の例に出てきたものから順に見ていきます。
キーの順番
JSON のオブジェクトは、キーの順番に意味を持ちません。
{"a":1,"b":2} と {"b":2,"a":1} は同じ値です。
ところが行で比べると、並びが違うだけで別の行になります。 データベースから取り出した順や、プログラムが組み立てた順で、並びは簡単に入れ替わります。
空白・改行・字下げ
JSON では、記号の前後の空白・タブ・改行は意味を持ちません(RFC 8259 §2)。 1 行に詰めた JSON と、字下げした JSON は同じ値です。
Python の例では、字下げをしないときは ", " と ": " のように空白を入れて書きます(Python ドキュメント)。
空白の入れ方が違うだけで、全行に印が付きます。
文字のエスケープ
"佐藤" と "佐藤" は、エスケープを解くと同じ文字列です(RFC 8259 §8.3)。
"https:\/\/a\/b" と "https://a/b" も同じです。
Python は日本語を既定で \u の形にし、PHP はスラッシュも既定でエスケープします(両者のドキュメント)。
人が読むと別物に見えるので、いちばん気づきにくい見かけの差分です。
数値の書き方
80、80.0、8e1 は、どれも同じ 80 です。
PHP は既定で 80.0 を 80 と書き、Python は 80.0 のまま書きます。
PHP マニュアルの JSON_PRESERVE_ZERO_FRACTION の説明にある挙動です。
小数点以下が 0 の数値は、出力する言語が変わるたびに書き方が揺れます。 値は変わっていないので、差分として扱う必要はありません。
配列の並び
配列は、ほかの 4 つと事情が違います。 JSON の配列は、並び順に意味があるデータです。
ランキングや手順の一覧なら、並びが変わったことは本物の差分です。 一方、ユーザーの一覧のように id で区別できるデータなら、並びの違いは見かけの差分にすぎないこともあります。
どちらかは、JSON だけでは決まりません。 データの意味を知っている人が、比べ方を選ぶ必要があります。
5 つの原因と、そろえ方の対応表
5 つの原因を、行で比べたときの見え方と、見かけの差分を消す方法で並べます。
| 原因 | 行で比べたときの見え方 | 見かけの差分を消す方法 |
|---|---|---|
| キーの順番 | 同じ行が別の位置に出る | キーを並べ替えてから比べる/値で比べる |
| 空白・改行・字下げ | ほぼ全行に印が付く | 同じ設定で整形し直す/値で比べる |
| 文字のエスケープ | 日本語や / を含む行に印が付く | エスケープを解いて書き直す/値で比べる |
| 数値の書き方 | 小数点以下が 0 の数値の行に印が付く | 値で比べる(表記は別に確かめる) |
| 配列の並び | 要素が入れ替わった範囲に印が付く | 並びに意味が無いときだけ、キーで突き合わせる |
表の左から 2 列目は、その原因があるときの見え方です。 逆向きには使えません。
同じ見え方は複数の原因から生まれます。 ほぼ全行に印が付いたからといって、原因が字下げだけとは限りません。
値で比べても消えてしまう差
見かけの差分とは反対に、比べる道具によっては本物の差が消えることもあります。
値で比べる道具が、JavaScript の JSON.parse と同じ方法で数値やキーを読んでいる場合です。
大きな整数の末尾
JSON.parse は、数値を倍精度の浮動小数点数として持ちます。
そのため、次の 2 つは読み込んだ時点で同じ値になります。
12345678901234567890 と 12345678901234567891 です。
RFC 8259 は、倍精度で扱う実装どうしで値が確実に一致する整数を、絶対値が 2 の 53 乗未満の範囲としています。 20 桁の ID のような値は、この範囲の外にあります。
同じキーが 2 回ある JSON
{"a":1,"a":2} のように同じキーが 2 回ある JSON は、JSON.parse で読むと後の値 2 だけが残ります。
前の 1 は、比べる前に消えます。
RFC 8259 は、キーを重複させないよう求めています。 重複したときの扱いは実装ごとに違い、多くの実装は最後の組だけを使うと書かれています。
比べる前に決めておくこと
JSON を比べる前に、「字面の違いも知りたいか」「値の違いだけを知りたいか」を決めておきます。
設定ファイルの書式まで管理しているなら、字面の違いにも意味があります。 その場合は、整形とキーの並べ替えをそろえてから行で比べます。
API の返す中身が変わったかを知りたいなら、値で比べるのが近道です。 JSON比較は値で比べ、書き方だけ違う数値や重複キーは差分とは別に知らせます。
まとめ
JSON を比べて差分だらけになる原因は、キーの順番・空白・文字のエスケープ・数値の書き方・配列の並びの 5 つです。 前の 4 つは値が同じなので、そろえてから比べるか、値で比べれば消えます。
配列の並びは、データによって本物の差分にも見かけの差分にもなります。 大きな整数と重複キーは、値で比べる道具でも消えることがあるので、字面を確かめる手段を残しておきます。
参考・出典
- RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format — RFC Editor (2026-09-29 閲覧)
- json --- JSON エンコーダおよびデコーダ — Python ドキュメント (2026-09-29 閲覧)
- json_encode — PHP マニュアル (2026-09-29 閲覧)
本記事は上記の公的機関・公式情報源を参考にしています。最新の情報は各リンク先をご確認ください。
あわせて読みたい
同じテーマ・関連ツールの記事です。