ステージングAPIは200を返します。プロダクションAPIも200を返しますが、どこかの下流で問題が発生し、あなたは二つのJSONブロブを比較して何が変わったかを調べようとしています。
JSONの比較は、実際に比較しているときに簡単には見えません。
JSON比較が見えていない理由
JSONには標準形式がありません。二つのオブジェクトは同じデータを表しても、実際に見るとまったく異なる場合があります。開発者に bite する点はここです:
キーの順序。 JSONオブジェクトは仕様上順序がありません — {"a":1,"b":2} と {"b":2,"a":1} 意味的に同じですが、raw stringとして比較すると異なるように見えます。
空白文字。 最小化されたJSONと整形されたJSONは文字列比較で失敗します。同じデータでも異なるバイトです。
整数の列はJSONの数値に変換され、true/falseの値を持つ列は論理値に変換されます。しかし、ZIPコードのような整数に見える列(90210)は文字列として保持すべきです — 変換すると先頭のゼロが失われます。 "1" と 1 異なるJSON値です。そして null 欠落したキーも同じです。あなたの差分ツールはこの違いを考慮する必要があります — そしてあなたも、APIの消費者が同じように扱わない可能性があるためです。
ネストの深さ。 大きな応答の中の5段階深い変更値は、raw出力を見ているときに簡単に見過ごされてしまいます。
構造的等価性と意味的等価性
APIの変更をデバッグするときにこの区別は重要です。
構造的等価性 正規化後のJSONがバイトごとに完全に一致することを意味し、同じキー、同じ値、同じ順序です。キャッシュ検証や署名チェックに役立ちます。
意味的等価性 データが同じことを表していることを意味し、構造が異なる場合でもそうです。応答が user_id に userIdをリネームしたり、新しいオプションフィールドを追加したりしても、意味的に異なるものの、消費者にとって機能的に同等である可能性があります。
リグレッションを検出するときは、通常は構造的等価性が必要です。API消費者の破壊的な変更を評価するときは、意味的等価性が適切なフレームです。
ターミナルでJSONを差分比較する方法
の場合、クッキーは jq と diff
jq キーをソートし、空白文字を正規化することで、差分比較の前にしっかりした前処理ステップになります:
diff <(jq --sort-keys . response_v1.json) <(jq --sort-keys . response_v2.json)
これはキーの順序とフォーマットを処理します。実際のデータの違いだけを表示します。さらに -c を追加してコンパクトな差分出力を得たり、 -u を追加して統合形式を得ることができます。
ライブAPI応答と保存されたベースラインを比較する場合:
diff <(jq --sort-keys . baseline.json) <(curl -s https://api.example.com/endpoint | jq --sort-keys .)
Pythonの deepdiff
構造化出力が必要な場合 — 特にネストされたオブジェクトや配列の場合 — deepdiff 変更をプログラム的に見ることができます:
from deepdiff import DeepDiff
import json
with open("response_v1.json") as f:
v1 = json.load(f)
with open("response_v2.json") as f:
v2 = json.load(f)
diff = DeepDiff(v1, v2, ignore_order=True)
print(diff.to_json(indent=2))
DeepDiff 変更を分類します: values_changed, dictionary_item_added, dictionary_item_removed, type_changes。これはCIでのリグレッションチェックをスクリプト化しやすくなります。
インストール方法は: pip install deepdiff
一般的な使用例
環境間のAPI応答の比較。 ステージングとプロダクションは同じ構造を返すべきです。デプロイ後にすぐにjqで差分を取ることで、ユーザーが実際に気づく前にスキーマの変更を検出できます。
時間経過によるスキーマの変化を検出する。 APIは進化します。保存されたベースラインをピン留めし、毎回デプロイ時に差分を実行することで、正確にいつ何が変わったかを追跡できます — バグレポートから発見するのではなく。
リグレッションテスト。 期待される応答を記録し、API呼び出しを再現し、出力を差分比較します。これは特にスキーマをコントロールできない第三者APIに非常に有用です。
配列比較の課題
配列はJSON差分ツールが混乱する場所です。仕様上、JSON配列の順序は重要であり、ほとんどの差分ツールは再順序された配列を個々の変更値として扱い、混乱し、ノイズを生み出します。
APIがタグのリストを返す場合、タグが異なる順序で返された場合、単純な差分はすべての要素を変更として表示します:
- "tags": ["json", "api", "rest"]
+ "tags": ["api", "json", "rest"]
ツールのように deepdiff を設定できます。 ignore_order=True 配列をデフォルトでソートしないので、既知の配列フィールドに対して jq をパイプ通す必要があります。 sort 実用的なルール:APIで配列の順序が意味的に重要でない場合(たとえばタグリストの場合)、順序を無視できる差分ツールを使用します。順序が重要である場合(たとえばソートされた結果リストの場合)、それを無視しないでください。
JSONスキーマ検証を使うべきタイミング
差分は瞬間的な比較です — 二つの特定の応答の違いを示します。JSONスキーマ検証は応答が契約に従っているかどうかを示します。
次の場合はJSONスキーマ検証を使う:
すべての応答に対して構造を強制したい場合
- 公開APIを発表し、バックワード互換性を保証したい場合
- 欠落した必須フィールドや誤った型を検出したい場合(値の変更ではなく)
- 差分ツールを使うべき場合は:
二つの特定の応答があり、何が変わったかを理解したい場合
- APIバージョン間のリグレッションをデバッグしたい場合
- デプロイをチェックしたい場合
- これらは異なる問題を解決します。本格的なAPIテストでは、両方を使うべきです。
速い選択肢:IO Tools JSON Compareを使う
すぐに、ターミナルを使わずにブラウザベースの差分を実行したい場合、
IO Tools JSON Compare キーの順序、空白文字の正規化、ネストされたオブジェクト、型認識比較の一般的なケースを処理します。二つのJSONオブジェクトを貼り付けて、クリーンな並列差分を得ることができます。 デバッグ中でターミナルを開くのが面倒な場合に非常に便利です。
迅速なリファレンス:異なるアプローチが何を検出するか
文字列差分
| シナリオ | キーの順序が異なる | jq + diff |
deepdiff |
JSONスキーマ |
|---|---|---|---|---|
| 欠落 | 検出 | 追加の空白文字 | 追加の空白文字 | 検出 |
| 型の不一致( | 検出 | 追加の空白文字 | 追加の空白文字 | 検出 |
null と欠落したキー"1" vs 1) |
追加の空白文字 | 追加の空白文字 | 追加の空白文字 | 追加の空白文字 |
| 配列の順序変更 | 追加の空白文字 | 追加の空白文字 | 追加の空白文字 | 追加の空白文字 |
| 誤検出(False positive) | 設定可能 | 設定可能 | 追加されたオプションフィールド | 検出 |
| スキーマ契約違反 | 追加の空白文字 | 追加の空白文字 | 追加の空白文字 | 追加されたオプションフィールド |
| デバッグする内容に応じて正しいツールを選択します。簡単なチェックには、 | 検出 | 検出 | 検出 | 追加の空白文字 |
および jq --sort-keys がほとんどのケースをカバーします。CIでのリグレッションテストには、 diff が構造的でスクリプト可能な出力を提供します。スキーマの強制にはJSONスキーマ。そして、ターミナルを開く前に答えをすぐに得たい場合、ブラウザベースのJSON比較差分ツールが数秒で解決します。 deepdiff 構造化された、スクリプト可能な出力を提供します。スキーマの強制にはJSONスキーマが使用され、ターミナルを開く必要がない場合でも、ブラウザベースのJSON比較差分ツールにより、数秒で答えを取得できます。
恵 スコアボードが到着しました!
スコアボード ゲームを追跡する楽しい方法です。すべてのデータはブラウザに保存されます。さらに多くの機能がまもなく登場します!
