フォーマットの前に必ず検証する
整形されていても JSON として無効ならファイルは壊れたままです。インデントを変えるだけで何かが直ったと思う前に、厳密なバリデーター(本サイトの JSON Formatter & Validator など)で検証してください。よくあるパースエラーは、末尾カンマ(標準 JSON では不可)、シングルクォート(ダブルクォートのみ有効)、キーの引用符忘れ、コメント混入などです。JSON にはコメント構文がありません。コメントが欲しければ「_comment」のようなラッパーキーを使うか、コメントを許容するフォーマット(JSON5・YAML・TOML)に切り替えてください。
命名規則を 1 つに決めて徹底する
ほとんどの JSON API は lower_snake_case か camelCase に収束します。プロジェクトでひとつ選び、隅々まで揃えてください。混在すると消費側を混乱させ、コードジェネレーターも壊れます。snake_case は Python / Ruby 系で多く、camelCase は JavaScript / Java 系で多いです。PascalCase は型付き言語からの生成出力でたまに見るくらいで稀です。kebab-case はキーには避けてください(JavaScript からはブラケット記法でしかアクセスできません)。
数値の扱いに注意する
- JSON は整数と浮動小数点数を区別しません。多くのパーサは小数点の有無で型を決めます。
- JavaScript は 2^53 を超える整数で精度を失います。64 ビット ID は文字列としてシリアライズしてください。
- 先頭のゼロ(無効な JSON)と末尾の小数点(非ポータブル)は避けてください。
- 通貨額は文字列、または最小単位(円・セント)の整数として送り、浮動小数点誤差を防ぎます。
- NaN や Infinity は JSON として無効です。シリアライズ前に null や文字列へ変換してください。
順序付きコレクションは配列、キー検索はオブジェクト
配列は「順序が意味を持つ・イテレートする」ことを示し、オブジェクトは「キーアクセス・順序は関係ない」ことを示します。リストを数値文字列キー(「0」「1」「2」)のオブジェクトとして表現するのは古いシリアライザの遺物で、消費側がキー順序からリスト順序を再構築する必要があり脆弱です。逆に、本質的にキー付きのデータを 2 要素ペアの配列でフラット化するのも、オブジェクトのほうが明確であれば避けましょう。
null・キー欠落・空配列を区別する
3 つの状態には別々の意味があります。キーが存在しない(フィールドが提供されなかった)、null(提供されたが値なし)、空配列(提供されたが要素なし)。これらを同じものとして扱うバグは頻発します。API の各フィールドで返しうる状態を文書化し、本当に該当しないフィールドは null ではなくキー自体を省略するのが望ましいです。
進化を前提に設計する
JSON API は時間とともにフィールドを追加していきます。消費側は未知のフィールドを無視する設計に、生成側は既存フィールドの意味を変えずに新しいオプションフィールドを追加する設計にしてください。互換性を破る変更は、エンドポイントの新バージョン(バージョン付き URL や Content-Type)として明示してください。
コントラクトには JSON Schema を使う
2 つのシステムが JSON をやり取りする場合、JSON Schema 文書は両側でマシン検証可能な契約になります。スキーマには必須フィールド・型・許容値・フォーマット・フィールド間の検証ルールまで記述できます。ドキュメント生成・クライアント生成・実行時検証に利用でき、内部サービスでも軽量なスキーマがあるほうが何もないより遥かに保守しやすくなります。
人間向けはプリティ、機械向けはミニファイ
設定ファイル・人間が読むログ・バージョン管理に入れるファイルは、2 / 4 スペースで揃ったインデントで整形してください。帯域が重要なネットワークペイロードはミニファイします。ほとんどの JSON ツールはデータを変えずに両者を切り替えられます。途中で混在させると差分にノイズが入るので避けてください。
信頼できない入力には防御的に
JSON のパースに eval() を使ってはいけません。JavaScript なら JSON.parse()、各言語の安全なパーサを使ってください。受信ペイロードに妥当なサイズ上限を設けます。ネスト深度攻撃にも注意が必要です。多くのパーサは数千レベルの深いネストを受け入れてしまい、素朴な消費側をクラッシュさせ得ます。妥当なネスト深さを超えるペイロードは拒否してください。