JSONとは何か
JSON(JavaScript Object Notation)はRFC 8259で定義されたテキストベースのデータフォーマットです。名前に反して、JavaScriptの実行とは無関係です——構造化データをプレーンテキストとして表現する方法にすぎません。すべての主要プログラミング言語が読み書きできます。
このフォーマットは2001年頃にDouglas Crockfordによって形式化されましたが、XMLよりシンプルでありながら目視でデバッグできる程度に人間が読めるため普及しました。今日ではREST APIのデフォルトのワイヤーフォーマット、設定ファイル(package.json、tsconfig.json)、MongoDBのようなドキュメントデータベースで使われています。
意外に思う人もいますが、JSONはYAML 1.2の厳密なサブセットです。有効なJSONファイルはすべて有効なYAMLでもあります。逆は成り立ちません——YAMLはコメント、複数行文字列、アンカーなどJSONがサポートしない機能を許可します。
JSONの構文ルール(本当に重要なもの)
JSONのデータ型は正確に6つ:文字列、数値、真偽値(true/false)、null、オブジェクト(キーと値のペア)、配列(順序付きリスト)。以上です。日付型もundefinedも関数もありません。
文字列はダブルクォートを使わなければなりません。シングルクォートは無効です。これがJavaScriptオブジェクトをJSONファイルに貼り付けたときに「unexpected token」エラーが出る第1の理由です。
数値は先頭ゼロ不可(01は無効)、16進表記不可(0xFFは無効)、NaNやInfinityも不可です。これらの値が必要なら文字列としてエンコードしましょう。
オブジェクトは波括弧{}を使い、キーと値のペアをコロンで区切ります。キーはダブルクォートで囲まれた文字列でなければなりません。最後の項目の後の末尾カンマは違法です——手動編集のJSONで最もよくある構文エラーです。
// ❌ INVALID — these mistakes are everywhere
{
name: "Alice", // keys must be quoted
'age': 30, // single quotes not allowed
"scores": [1, 2, 3,], // trailing comma
"id": 0x1F, // hex numbers not allowed
}
// ✅ VALID JSON
{
"name": "Alice",
"age": 30,
"scores": [1, 2, 3],
"id": 31
}よくあるJSONエラー5選(と修正方法)
1. 末尾カンマ:オブジェクトまたは配列の最後の項目の後のカンマを削除します。ほとんどのフォーマッターが即座にハイライトします。末尾カンマが欲しいなら、VS Code設定ではJSONC(JSON with Comments)を使いましょう。
2. Unexpected token at position 0:通常、レスポンスがそもそもJSONではないことを意味します——HTMLエラーページ(<で始まる)か空文字列かもしれません。APIエンドポイントとContent-Typeヘッダーを確認しましょう。
3. クォートなしのキー:JavaScriptコンソール出力からコピーすると{"name": "value"}ではなく{name: "value"}になります。フォーマッターに通せば自動的にクォートが追加されます。
4. シングルクォートの文字列:Pythonのjson.dumps()は常にダブルクォートを生成しますが、手書きやPythonのrepr()からコピーするとシングルクォートになることがあります。文字列にアポストロフィが含まれる場合、単純な検索置換は機能しません——適切なフォーマッターを使いましょう。
5. BOM(バイトオーダーマーク):一部のWindowsエディタが不可視の\uFEFF文字を先頭に付加します。JSONパーサーはこれで失敗します。バイナリエディタでファイルを開いて、EF BB BFで始まっていればBOMです。BOMなしのUTF-8で保存しましょう。
JSON vs JSONC vs JSON5 vs YAML:いつ何を使うか
標準JSON(RFC 8259):APIペイロード、データ交換、最大の互換性が必要なものに使います。すべての言語のすべてのパーサーがサポートしています。
JSONC(JSON with Comments):手動で編集する設定ファイルに使います——VS Code設定、tsconfig.jsonなど。//と/* */コメントおよび末尾カンマを許可します。標準のJSON.parse()ではサポートされません。
JSON5:クォートなしのキー、シングルクォート文字列、16進数、末尾カンマ、コメントでJSONを拡張します。人間が編集する設定ファイルに適しています。パースにはjson5 npmパッケージが必要です。
YAML:コメント、複数行文字列、アンカー/エイリアスによるDRY設定が必要なときに使います。DevOpsで一般的(Docker Compose、Kubernetes、GitHub Actions)。注意:インデントエラーは無言で致命的です。「ノルウェー問題」(NOがfalseとしてパースされる)は多くのチームを苦しめてきました。
推奨:マシンが読むものには標準JSONを使いましょう。人間が定期的に編集するものにはJSONCまたはYAMLを使いましょう。チームが既に依存していない限りJSON5は使わないでください。
大きなJSONファイルの扱い
ブラウザベースのツール(私たちのものを含む)は約50MBまでのファイルを扱えます。それ以上はタブが重くなり始めます。より大きなファイルにはコマンドラインツールを使いましょう:クエリにjq、フォーマットにpython -m json.tool、インタラクティブな探索にfx。
100MB以上のJSONファイルを定期的に扱うなら、JSONが正しいフォーマットかどうか検討しましょう。NDJSON(改行区切りJSON、1行1オブジェクト)はストリーム処理可能で、ファイル全体をメモリに読み込む必要がありません。jqのようなツールはNDJSONを1行ずつ処理できます。
APIレスポンスにはページネーションが味方です。エンドポイントが1レスポンスで10,000オブジェクトを返すなら、それはAPI設計の問題であってJSONの問題ではありません。小さなページをリクエストしてインクリメンタルに処理しましょう。
パフォーマンスのヒント:V8のJSON.parse()は大きなオブジェクトに対してJavaScriptオブジェクトリテラルより実際に速いです(2019年以降、直感に反しますが事実)。フロントエンドコードに大きな静的データセットがある場合、JSON.parse('...')でラップするとスタートアップ時間を改善できます。
JSONのセキュリティ考慮事項
JSONのパースにeval()を使ってはいけません。JSON.parse()が存在する前の2000年代初頭には一般的でしたが、任意のコードを実行します。常にJSON.parse()または言語の同等機能を使いましょう。
JavaScriptではプロトタイプ汚染に注意しましょう。{"__proto__": {"isAdmin": true}}をパースしてオブジェクトにマージすると、Object.prototypeを誤って変更する可能性があります。lodash.mergeにはこの脆弱性がありました。制御できないパースデータにはObject.create(null)を使いましょう。
JSONは循環参照をサポートしません。自分自身を参照するオブジェクトをJSON.stringify()しようとするとTypeErrorが発生します。循環構造を扱うにはreplacer関数またはflattedのようなライブラリを使いましょう。
サイズ制限はAPIにとって重要です。10MBのJSONペイロードはDoS攻撃のベクターになり得ます。サーバーにContent-Length制限を設定しましょう(Express: express.json({limit: "1mb"})、nginx: client_max_body_size)。
異なる環境でのJSONバリデーション
クイックバリデーション:JSON Formatterツールに貼り付けましょう。構文エラーの正確な行と文字位置を表示し、最大50MBのファイルを扱い、オフラインで動作します。
スキーマバリデーション:JSON Schema(draft 2020-12)を使ってJSONが従うべき構造を定義します。ajv(JavaScript)やjsonschema(Python)のようなツールがランタイムでスキーマに対してデータを検証します。これは構文チェックでは見逃す「有効なJSONだが構造が間違っている」エラーを検出します。
CI/CDパイプライン:JSONリントステップを追加しましょう。最もシンプルなアプローチ:python -m json.tool < file.json > /dev/null。スキーマバリデーション付きのより厳密なチェックにはajv-cliやcheck-jsonc-syntaxを使います。
設定ファイル:ほとんどのフレームワークは独自の設定フォーマットを検証します。next buildはnext.config.jsの問題を検出し、tsc --noEmitはtsconfig.jsonの問題を検出します。ツールチェーンが既に提供しているバリデーションを再発明しないでください。