データ形式・構造化データ
設定変更の前後で、どの値が変わった?
使うツール
設定を更新した人は『待ち時間を変えただけ』と言っているのに、レビュー画面では何十行も変更されている。整形ツールを通したり、保存時にキーの並びが変わったりすると、こうしたことが起きます。行ごとの比較だけを追っていると、本当に確認したかった変更が見えなくなります。
JSON構造比較では、二つの入力をJSONとして読み、項目ごとの追加・削除・変更を調べます。ただし『同じ』の意味は一つではありません。ファイルの文字が同じなのか、キーと値が同じなのか、実際のアプリケーションの動作が同じなのか。この記事では、短い架空の設定を使って、その境界を確かめます。
二つのファイルは、同じ範囲を表している?
比較を始める前に、二つの入力が何の時点、どの環境、どの範囲の設定かを確認します。片方は設定の全体、もう片方は変更部分だけのJSONなら、大量の削除が出ても不思議ではありません。比較器は『省略しただけ』と『削除した』を入力だけから区別できないためです。必要な部分を切り出すなら、両方で同じ親の下を選び、切り出した範囲を記録します。
APIのレスポンスを比べる場合は、取得時刻や毎回変わる識別子が混ざっていないかも見ます。差分を減らすために削るのではなく、今回の問いに含める値かどうかを判断します。時刻を除外してよいのは、時刻の変更を調べていない場合だけです。元データは手元の作業用コピーとして残し、除外した項目と理由を比較結果に添えると、後から検証範囲を説明できます。
空白にも二種類あります。キーの外側のインデントは構造を変えませんが、"test"と"test "の末尾の空白は文字列の一部です。利用先がそれを除去するかどうかは別問題なので、差分を消すために全ての文字列をtrimするような前処理は避けます。数値も、このツールでは表記の違いを検出することがあります。1と1.0を同じ金額として扱う判断と、設定ファイルに同じ表記が保存されているかの確認は分けて考えてください。
まず、何も変更していない例を比べる
JSON構造比較の左側へ変更前、右側へ変更後を入力します。まずは次の二つをそれぞれの欄へ貼り付けてください。文字列の並びは違いますが、どちらも同じ三つの設定を持っています。左と右を逆にしてもこの例の結論は変わりませんが、実際の変更を調べるときは方向をそろえると読み違いを防げます。
{"timeout":30,"region":"test","enabled":true}{"enabled":true,"region":"test","timeout":30}実行すると差分の合計は0になります。改行やインデントを変えても、値の外側の空白だけなら同じ結果です。ここで分かるのは、入力したJSONの比較対象に変更がないことです。文字列として完全一致しているわけではないため、ファイルのハッシュ値が同じになるとは限りません。設定のレビューとファイルの同一性確認では、選ぶ道具が違います。
一つだけ値を変え、差分の方向を読む
{"enabled":true,"region":"test","timeout":60}変更は30から60への一つです。結果のbeforeとafterを見て、想定した方向になっているか確認します。数値の内部表現に由来する階層が結果のパスへ現れる場合があるため、パスの末尾だけで設定名を判断せず、親のtimeoutと変更前後の値を合わせて読んでください。差分画面の表現と、元のJSONで変更すべき位置は必ずしも同じ文字列ではありません。
次に左と右を入れ替えると、今度は60から30への変更になります。追加と削除も反転します。『変更後に消えた項目』を探しているのに左右を逆に置くと、追加されたように読んでしまいます。比較結果を共有するときも、左が旧設定、右が新設定であることを添えるだけで、読み手の迷いを減らせます。
項目がないことと、nullであることは違う
{"label":"demo","note":null}{"label":"demo","memo":""}この例ではnoteが削除され、memoが追加されます。空文字列、null、項目そのものがない状態は、画面で空欄に見えても同じではありません。設定を省略すると既定値を使い、nullを指定すると機能を無効にする、といったアプリケーションもあり得ます。比較ツールはこの違いを見つけられますが、どちらの設定が正しいかまでは決められません。
同じ理由で、trueを文字列の"true"へ変えることも単なる表記変更ではありません。移行作業では、件数が少ない差分ほど安全とは考えないでください。一つの型の変更が、何十行もの並び替えより大きな動作の違いを生むことがあります。重要なのは差分数の少なさより、利用先がその項目をどう解釈するかです。
配列の先頭へ一つ足すと、なぜ差分が増える?
{"steps":["build","test","deploy"]}{"steps":["prepare","build","test","deploy"]}配列の順序を考慮すると、同じ位置の要素どうしを比較します。0番目はbuildからprepare、1番目はtestからbuild、2番目はdeployからtestへ変わり、最後にdeployが追加された形です。人間には『先頭へ一件追加』と見えても、位置ごとの比較では変更3件と追加1件になります。表示された4件を、そのまま4回の編集操作と数えないでください。
このツールは、配列内のidを使って同じ商品や同じ利用者を自動的に対応付ける専用比較ではありません。大量の行がずれたときは、まず先頭付近への挿入・削除を疑います。行番号だけを追う代わりに、元データの識別子や周辺の要素を見て、何が移動したのかを確かめましょう。
順序を無視する前に、その順序の意味を考える
{"tags":["blue","green"]}{"tags":["green","blue"]}順序を考慮する設定では変更2件、順序を無視する設定では差分0件になります。単に付いているタグの種類を調べたいなら、後者は役立ちます。しかし、処理手順や優先順位の配列で同じ設定を使うと、重要な変更を見落とします。testの後にdeployすることと、deployの後にtestすることは、同じ要素を持っていても同じ仕事ではありません。
また、順序を無視する設定は重複削除ではありません。同じタグが二つある状態と、一つだけある状態は分けて確認します。現在の比較は入れ子の配列も並べ替えるため、外側だけ順序を無視して内側の手順は保持する、といった個別指定には使えません。配列が複数ある設定では、まず順序を考慮した結果を残し、補助的な確認として切り替えると安全に読み取れます。
差分ゼロでも、動作保証にはならない
同じ設定でも、アプリケーションの版、環境変数、外部ファイルが違えば動作は変わります。この比較に入力していない情報は調査対象になりません。また、差分の多さから危険度を自動判定する機能もありません。『この二つのJSONでは、指定した比較条件のもとで何が違うか』という狭い問いに答える道具として使うと、結果を過信せずに済みます。
レビューを終える前に、変更した値、追加・削除した項目、配列順序を無視したかどうかを一緒に記録します。もし予想外の差分があれば、両方をJSON整形で読みやすくして該当部分を確認します。必須項目や型まで検査したい場合は、対応範囲を確認してJSON Schema検証へ進みます。差分を見つける作業と、変更を受け入れてよいかの判断を分けることが、レビューを短く、確実にするコツです。