2つのJSONファイルを比較して変更点を見つける方法
2つのJSONファイルを比較する最速の方法は、両方をサイドバイサイドのdiffツールに貼り付け、 同じ形式にフォーマットして、ハイライトされた行を読むことです。難しいのは比較そのものではなく、 ノイズの処理です。キーの並び替え、インデントの違い、不要な末尾カンマによって、 ほぼ同一のファイルがまるで共通点がないように見えてしまいます。
このガイドでは、クリーンで信頼できるdiffを取得する方法を解説します。 JSONファイルが意味的に同一でも見た目に差異が生じる理由、知っておくべきいくつかの手法、 そして実際に試せるワークド・エグザンプルを紹介します。ツールだけが必要な場合は、 JSON比較ページでブラウザ上のすべてを処理できます。
JSONファイルの比較が意外と難しい理由
JSONは小さく厳格な文法を持っていますが(仕様は json.orgを参照)、 テキストのレイアウト方法については多くの自由度があります。2つのファイルがまったく同じオブジェクトを 記述していても、バイト単位では異なる場合があります。プレーンテキストのdiffはそのことを知らないため、 すべての差異を忠実にフラグとして立てます。
比較を始める前に理解しておくべき重要な点があります。JSONオブジェクトのキーには順序がありません。
仕様(RFC 8259)では、
オブジェクトを名前と値のペアの順序なし集合として定義しています。そのため、
{"name":"Ada","id":7}と{"id":7,"name":"Ada"}は
等価ですが、行ベースのdiffでは赤と緑に塗られてしまいます。
| diffで見えるもの | 本当の変更か? | 対処法 |
|---|---|---|
| キーの順序が異なる | いいえ、オブジェクトは順序なし | 両側のキーをソートする |
| 2スペースvs 4スペースのインデント | いいえ | 両側を同じ形式にフォーマットする |
| minified版 vs pretty-print版 | いいえ | 両側をフォーマットする |
| ファイル末尾の改行 | いいえ | 無視するか、空白をトリムする |
値が"7"から7に変わった | はい、文字列vs数値 | 調査が必要、これは本物の変更 |
| 配列の要素の順序が異なる | 場合による、配列は順序あり | ここで順序が重要かどうかを判断する |
最後の行はよく見落とされます。配列は順序を保持しますが、オブジェクトは保持しません。
つまり、[1, 2, 3]と[3, 2, 1]は本当に異なりますが、
オブジェクト内のキーは自由に入れ替わることができます。JavaScriptがこれをどう解析するかの詳細は、
MDNに優れた
JSONオブジェクトのリファレンスがあります。
JSONを比較する4つの方法とその使い分け
唯一の最良の方法はありません。ファイルがどこにあるか、何を調べたいかによって異なります。 よく使われる方法を比較します。
| 方法 | 向いている場面 | 手間 | JSONを理解するか? |
|---|---|---|---|
| 目視確認 | 小さなファイル、1〜2フィールド | 低 | いいえ、あなたがパーサー |
| オンラインdiffツール | クイックチェック、どこからでも貼り付け可能 | 低 | フォーマット+キーソートで対応 |
コマンドライン(jq、diff) | ディスク上のファイル、スクリプティング、大きなファイル | 中 | 最初にソートすれば対応 |
IDEまたはgit diff | リポジトリ内のファイル | コミット済みなら低 | デフォルトは行ベース |
ほとんどの人にとって、ブラウザツールはインストール不要でログやAPI呼び出しのスニペットを 直接貼り付けられるため、速度面で優れています。問題はフォーマットのノイズで、次で解決します。 ターミナルで作業する場合は、jq が使うべきツールです。重要なフラグを1つ紹介します。
最速のクリーン比較、ステップバイステップ
誰かが2つの設定ファイルを渡して「何が違うの?」と聞いてきたときに使うルーティンです。 約15秒で完了します。
- JSON比較ツールを開く。
- 左に元のファイル、右に新しいバージョンを貼り付ける。
- 両側でフォーマットをクリックして、同じインデントを使用するようにする。
- キーのソート(正規化)をオンにして、キーの並び替えが変更として表示されないようにする。
- 結果を読む。緑が追加、赤が削除、変更された値はそれぞれ1つずつ表示される。
ステップ3と4がポイントです。両方のファイルが同じ形式にフォーマットされ、 キーがソートされると、ハイライトされるのは実際に変更された内容だけになります。 diffエンジンはGoogleの diff-match-patch を基盤としており、最初に行単位で比較するため、長いファイルでも高速に動作します。
実例
あるユーザーレコードへの変更をレビューしているとします。変更前:
{
"name": "Ada Lovelace",
"role": "editor",
"active": true,
"seats": 3
}
そして、チームメイトから受け取った変更後:
{
"active": true,
"name": "Ada Lovelace",
"role": "admin",
"seats": 5,
"team": "platform"
}
これをプレーンな行ベースdiffに入力すると、キーの順序が違うためほぼすべての行が移動したように見えます。 両方をフォーマットしてソートすると、実際の話は短くなります:
| フィールド | 変更前 | 変更後 | 変更 |
|---|---|---|---|
role | editor | admin | 変更 |
seats | 3 | 5 | 変更 |
team | — | platform | 追加 |
name | Ada Lovelace | Ada Lovelace | 変更なし |
active | true | true | 変更なし(移動のみ) |
本物の変更は3つ:ロールの昇格、シート数、新しいチームフィールド。
並び替えはノイズでした。editorからadminへの昇格は
レビューで捉えたい変更ですが、20行の誤検知に埋もれていると見逃しやすいです。
コマンドラインでフォーマットのノイズを除去する
ファイルがすでにディスクにある場合、同じ「フォーマットとソート」のアイデアを
2つの短いコマンドで実現できます。-Sフラグはjqにオブジェクトキーをソートするよう指示します。
パイプで通すことで両ファイルが正規化され、プレーンdiffが正確になります:
jq -S . old.json > old.sorted.json
jq -S . new.json > new.sorted.json
diff old.sorted.json new.sorted.json
これでdiffは本当に変更された値のみを報告します。両ファイルが同じインデントと
同じキー順序を持つためです。これはブラウザでフォーマットとキーのソートをクリックする
ターミナル版です。
テキストdiff vs 構造的diff
上記のすべてはテキストdiffです。高速で視覚的であり、変更を読む人間には最適です。
構造的diffはさらに進んで、変更をデータとして記述します。その標準はJSON Patchで、
RFC 6902に定義されており、
特定のパスでのreplaceやaddなどの操作として編集を表現します。
プログラムが変更を適用する必要がある場合(人間が目視するのではなく)に構造的diffが必要です。
日常のレビューでは、キーをソートしたテキストdiffで十分です。
注意すべき一般的な落とし穴
| 落とし穴 | なぜ問題になるか | 対処法 |
|---|---|---|
| 数値精度 | 2^53 − 1を超える整数はJavaScriptで精度を失う | 大きなIDは文字列として比較する。MAX_SAFE_INTEGERを参照 |
| Unicodeエスケープ | "café"と"café"は同じ文字列だが異なるバイト | 両側をフォーマットしてエンコードを正規化する |
| 重複キー | ほとんどのパーサーは最後のものを無音で保持する | diffを信頼する前にJSONを検証する |
| 末尾カンマ | 正しいJSONではない。一方のファイルがパースさえできない場合がある | まず構文を修正する。バリデーターがフラグを立てる |
| 文字列 vs 数値 | "5"と5は似ているが異なる型 | これは本物の変更。無視しないこと |
関連ツール
JSONだけを扱うことはほとんどありません。環境間で設定を比較するなら、 YAML比較が同じアイデアをYAMLに適用します。 2つのAPI呼び出し間の変更をレビューするなら APIレスポンスdiffが最適で、 依存関係のバンプは package.json diffページで最も読みやすくなります。
よくある質問
- JSONファイルをオンラインで比較するとどこかにアップロードされますか?
- comparetext.orgではdiffはブラウザで実行されます。2つのJSONファイルはあなた自身のマシン上のJavaScriptによって比較されるため、明示的に保存または共有をクリックしない限り、サーバーには何も送信されません。設定ファイル、APIレスポンス、キーストロークのたびにアップロードするランダムなウェブサイトに貼り付けたくないデータにも安全です。
- 2つのJSONファイルがすべての行で差異があると表示されるのはなぜですか?
- ほぼ常にフォーマットの問題であり、本物の変更ではありません。一方のファイルがminifiedまたはタブでインデントされており、もう一方は2スペース、あるいはオブジェクトキーの順序が異なります。両側でフォーマットをクリックして同じインデントを使用するようにし、次にキーをソートして順序が問題にならないようにします。その後、diffは本当に変更された少数の値に縮小されます。
- キー順序を無視してJSONを比較するにはどうすればよいですか?
- JSONオブジェクトキーには定義された順序がないため、
{"a":1,"b":2}と{"b":2,"a":1}は等価です。テキストdiffを一致させるには、比較前に両側のキーをソートします。ブラウザでは正規化(キーのソート)オプションを使用します。コマンドラインでは、jqで実行できます:jq -S . file.json。両ファイルのキーが同じソート順になると、本物の値の変更のみが表示されます。 - ページがフリーズせずに大きなJSONファイルを比較できますか?
- はい、ある程度まで。行モードdiffは、各文字ではなくまず行全体を比較するため、数千行のファイルでも高速です。非常に大きなファイル(数メガバイト)は、データをストリームするjqやgit diffなどのコマンドラインツールで処理する方が適しています。ブラウザで快適にスクロールできるものなら、オンラインdiffが速い選択肢です。
- JSONのテキストdiffと構造的diffの違いは何ですか?
- テキストdiffはファイルを行ごとに比較します。2つのエッセイを比較するのと同じ方法です。構造的diffはJSONを理解しているため、キーの並び替えは変更ではなく、配列内で移動した値は削除と追加ではなく移動だということを知っています。テキストdiffは高速で、ほとんどのレビューには十分です。構造的diff(例:RFC 6902によるJSON Patch)は、プログラムが適用できるデータとして変更を記述する必要がある場合に重要です。
- 2つのAPIレスポンスを比較するにはどうすればよいですか?
- 各レスポンスをファイルに保存するか、ブラウザのネットワークタブからコピーし、左に古いレスポンス、右に新しいレスポンスを貼り付けます。インデントが一致するよう両方をフォーマットし、APIが安定した順序でキーを返さない場合はキーをソートします。api-response-diffツールはまさにこのために最適化されています。2回の呼び出し間で名前が変更されたフィールド、変更されたステータスコード、文字列から数値に変わった値を見つけるのに役立ちます。
試してみませんか?JSON比較ツールにファイルを貼り付けて、何が変わったか確認しましょう。