JSON RFC 8259:本番のパーサーをつまずかせる細かな点
JSON はあなたが思うより小さい
RFC 8259 の JSON 文法はごく小さい:オブジェクト、配列、文字列、数値、そして3つのリテラル true、false、null。この単純さがどこにでもある理由であり——そして2つの驚きが人の不意を突く理由でもあります。第一に、任意の値が文書全体になれます:42、"hello"、true はそれぞれ完全で正当な JSON です。古い RFC 4627 はトップレベルがオブジェクトか配列であることを要求したので、一部の旧式ツールは今も裸の値を拒みます。
第二に、仕様はいくつかの挙動を意図的に未規定のままにします。構文は精密に定義しますが、重複キー・数値精度・順序についてはほとんど語りません。本番のパーサーはこれらの隙間を異なる形で埋め、相互運用性のバグはまさにそこに住みます。
重複キー:未定義の挙動
RFC 8259 はオブジェクト名が一意であるべき(SHOULD)だと言い、一意でなければならない(MUST)とは言いません。キーが重複すると結果は未規定なので、パーサーは分かれます:多くは最後の値を取り、一部は最初を保ち、いくつかはエラーを投げ、まれに両方を保ちます。仕様上どれも間違いではありません。
これは現実のセキュリティ上の危険です。あるサービスが最初の role を読んで {"role":"user","role":"admin"} を検証し、別のサービスが最後に従って動くなら、攻撃者は検証をすり抜けて権限を忍び込ませられます。決定が重要な箇所では重複キーをエラーとして扱い、どの値が「勝つ」かに決して依存しないこと。
数値:整数はなく、あるのは厄介事だけ
JSON の数値型は1つだけです。RFC は精度も範囲も定義せず——相互運用のため、実装は IEEE 754 倍精度に収まる値を扱うと期待される、とだけ述べます。実際には、2^53(すなわち 9007199254740992)を超える値はどれも精度を失いうる、という意味です。
古典的なバグ:9007199254740993 のような 64 ビット ID は、JavaScript で JSON.parse した後 9007199254740992 になります。数値が倍精度だからです。解決策は大きな整数を文字列として運ぶこと。人が忘れがちな規則がほかにも:先頭ゼロ不可(007 は不正)、先頭 + 不可、末尾の小数点不可(1. は不正)、そして NaN と Infinity は正当な JSON ではありません——null か文字列にシリアライズしてください。
文字列、Unicode、エンコーディング
文字列は二重引用符を使わねばならず、U+0000 から U+001F の制御文字はエスケープしなければなりません。スラッシュは任意で \/ とエスケープでき、だから一部のエンコーダは <\/script> を出力します——HTML の script タグを壊さない正当な方法です。RFC 8259 はシステム間で交換する JSON に UTF-8 を義務づけます。
基本多言語面(BMP)の外の文字、たとえば大半の絵文字は、単一ではなく \u エスケープのサロゲートペアで書かれます。孤立した、対になっていないサロゲートは技術的に不正ですが、一部のパーサーは受け入れます——あなたのデータがより厳格な実装に渡ったときにだけ現れる移植性の罠です。
JSON に見えて JSON でないもの
最もよくある誤りは JSON を JavaScript のように扱うことから来ます。末尾カンマは不正です。JSON にコメントは存在しません——JSON5 と JSONC はそれを足しますが、厳格な RFC 8259 パーサーは拒みます。単一引用符の文字列や引用符なしのオブジェクトキーも、どれだけ見慣れていても不正です。
より微妙なのがバイトオーダーマーク(BOM)です。RFC 8259 は生成側が BOM を追加してはならない(MUST NOT)、消費側が無視してよい(MAY)と言いますが、BOM は JSON の一部ではないので、ファイル先頭の UTF-8 BOM は厳格なパーサーを最初のバイトで失敗させえます。意味を持たない空白は空白・タブ・改行(LF)・復帰(CR)に限られ——それ以外はありません。
要点
安全性や正しさの決定が値に依存するところでは重複キーを拒否すること。
64 ビット整数は文字列として運ぶこと——JavaScript のような倍精度パーサーで生き残るように。
オブジェクトキーの順序に依存しないこと——多くのパーサーが挿入順を保っても、仕様はオブジェクトを順序なしと定義します。
RFC 8259 の下では任意の値が正当なトップレベル JSON だと覚えておくこと——オブジェクトや配列だけではありません。
コメントや末尾カンマが欲しいなら、欲しいのは JSON5/JSONC です——厳格なパーサーがデータを見る前に変換しましょう。本サイトの JSON フォーマッターは厳格な RFC 8259 文法に照らして検証するので、これらの問題はすぐ現れます。