開発者のためのJSON:構文、検証、よくある間違い
· 12分で読めます
目次
JSONとは何か、なぜ重要なのか
JSON(JavaScript Object Notation)は、Web上でのデータ交換の事実上の標準となっています。その名前はJavaScriptとの関連を示唆していますが、JSONは完全に言語に依存せず、事実上すべての最新のプログラミング言語でサポートされています。
2000年代初頭にDouglas Crockfordによって最初に仕様化されたJSONは、XMLの軽量な代替として登場しました。そのシンプルさと人間の可読性により、API、設定ファイル、データストレージに最適なフォーマットとなりました。
今日、JSONはREST APIやNoSQLデータベースから設定ファイルやデータパイプラインまで、あらゆるものを支えています。JSONを徹底的に理解することは、単に役立つだけでなく、現代のソフトウェア開発に不可欠です。
クイックヒント:JSONはプログラミング言語でもデータベース形式でもありません。純粋にデータシリアライゼーション形式、つまり構造化データをテキストとして表現する方法です。
JSONの構文規則
JSONの構文は一見シンプルですが、その厳格さは多くの開発者を驚かせます。JavaScriptオブジェクトとは異なり、JSONは構文のバリエーションに対して一切の寛容性がありません。
以下は、コア構造を示す有効なJSONの例です:
{
"name": "RunDev",
"version": 2,
"features": ["formatter", "validator", "converter"],
"config": {
"theme": "dark",
"autoSave": true
},
"deprecated": null
}
譲れないルール
すべてのJSONドキュメントは、これらの厳格なルールに従う必要があります:
- キーは二重引用符で囲まれた文字列でなければならない —
"name"は有効ですが、nameや'name'はパースエラーを引き起こします - 文字列は二重引用符を使用する必要がある —
"hello"は機能しますが、'hello'は機能しません - 末尾のカンマは禁止 —
[1, 2, 3]は正しいですが、[1, 2, 3,]は失敗します - コメントは許可されない —
// commentも/* comment */も有効ではありません - undefinedではなくnullを使用 — JavaScriptの
undefinedはJSONには存在しません - 数値は特定の形式に従う — 先頭のゼロは禁止(
010ではなく10)、16進表記は禁止、InfinityやNaNも禁止
なぜこんなに厳格なのか?
JSONの厳格さは意図的なものです。曖昧さを排除することで、JSONは有効なJSONドキュメントがすべてのプラットフォームと言語で同一にパースされることを保証します。この予測可能性こそが、JSONをデータ交換において信頼できるものにしています。
コメントの欠如はしばしば批判されますが、これにより開発者は明確なキー名と構造を通じてデータを自己文書化することを余儀なくされます。コメントが必要な設定ファイルの場合は、JSONC(コメント付きJSON)またはJSON5の使用を検討してください。
プロのヒント:当社のJSONフォーマッターを使用して、一般的な構文エラーを自動的に修正し、デプロイ前にJSONが有効であることを確認してください。
JSONデータ型の理解
JSONは正確に6つのデータ型をサポートしています。この限定されたセットは、ほとんどのデータ表現のニーズをカバーしながら、フォーマットをシンプルに保ちます。
| 型 | 例 | 注記 |
|---|---|---|
| 文字列 | "hello world" |
二重引用符で囲む必要があります。エスケープシーケンスをサポート:\n、\t、\u0041、\"、\\ |
| 数値 | 42、3.14、-1、1e10 |
整数と浮動小数点数の区別なし。16進数、8進数、Infinityなどの特殊値は禁止 |
| 真偽値 | true、false |
小文字のみ。"true"は文字列であり、真偽値ではありません |
| Null | null |
小文字のみ。意図的な値の欠如を表します |
| 配列 | [1, "two", true] |
順序付きリスト。混合型を含むことができます(ただし同種の配列の方が明確) |
| オブジェクト | {"key": "value"} |
順序なしのキーと値のペア。キーは文字列である必要があり、一意であるべきです |
文字列とエスケープシーケンス
JSON文字列は、特殊文字のためのいくつかのエスケープシーケンスをサポートしています:
\"— 二重引用符\\— バックスラッシュ\/— スラッシュ(オプションですが有効)\b— バックスペース\f— フォームフィード\n— 改行\r— キャリッジリターン\t— タブ\uXXXX— Unicode文字(例:\u0041は「A」)
複数行の文字列はエスケープシーケンスを使用する必要があります。リテラルの改行は許可されていません:
{
"correct": "Line 1\nLine 2\nLine 3",
"incorrect": "Line 1
Line 2
Line 3"
}
数値:許可されるものと許可されないもの
JSON数値は、ほとんどのプログラミング言語よりも制限的です:
- 有効:
42、-17、3.14159、-0.5、1.23e10、1.23e-10 - 無効:
.5(0.5である必要があります)、010(8進表記)、0xFF(16進数)、Infinity、NaN
JSON仕様は精度の制限を定義していませんが、ほとんどの実装はIEEE 754倍精度浮動小数点数を使用しており、整数は2^53 - 1(9,007,199,254,740,991)まで安全です。
プロのヒント:JavaScriptの安全な整数範囲を超える大きな整数の場合、パース中の精度損失を防ぐために文字列として保存することを検討してください。
オブジェクトとキーの一意性
JSON仕様ではオブジェクトのキーは一意であるべきとされていますが、パーサーが重複をどのように処理すべきかは義務付けられていません。異なる実装は異なる動作をします:
- JavaScriptの
JSON.parse()は最後の出現を保持します - 一部のバリデーターは重複キーを完全に拒否します
- 他のものは最初の出現を保持します
ベストプラクティス:異なるパーサー間での予測不可能な動作を避けるため、常にキーが一意であることを確認してください。
よくある間違いとその回避方法
経験豊富な開発者でもJSON構文エラーを犯します。以下は最も頻繁な間違いとその修正方法です:
| 間違い | 誤り | 正しい |
|---|---|---|
| 単一引用符 | {'name': 'test'} |
{"name": "test"} |
| 末尾のカンマ | {"a": 1, "b": 2,} |
{"a": 1, "b": 2} |
| 引用符なしのキー | {name: "test"} |
{"name": "test"} |
| コメント | {"a": 1 // comment} |
コメントを削除するかJSONCを使用 |
| undefined値 | {"a": undefined} |
{"a": null} |
| 先頭のゼロ | {"port": 080} |
{"port": 80} |
| リテラル改行 | "line1 |
"line1\nline2" |
| 関数値 | {"fn": function(){}} |
サポートされていません—文字列を使用するか再構築してください |
JavaScriptオブジェクトの罠
JSONエラーの最大の原因は、それをJavaScriptオブジェクトリテラル構文のように扱うことです。見た目は似ていますが、重要な違いがあります:
// 有効なJavaScriptオブジェクト
const jsObj = {
name: 'test', // 引用符なしのキー、単一引用符
age: 30,
active: true,
getData: function() { return this.name; }, // 関数が許可されている
// コメントが許可されている
};
// 有効なJSON(文字列として)
const jsonStr = `{
"name": "test",
"age": 30,
"active": true
}`;
覚えておいてください:JSONはデータ形式であり、コードではありません。関数、コメント、実行可能なロジックを含めることはできません。
文字エンコーディングの問題
JSONはUTF-8、UTF-16、またはUTF-32でエンコードする必要があります。UTF-8が最も一般的で推奨されるエンコーディングです。注意すべき点:
- ファイルの先頭にあるBOM(バイトオーダーマーク)—一部のパーサーはこれを拒否します
- パース失敗を引き起こす可能性のある無効なUTF-8シーケンス
- エスケープする必要がある制御文字(U+0000からU+001F)
クイックヒント:当社のJSONバリデーターにJSONを貼り付けて、構文エラー、エンコーディングの問題、構造上の問題を即座に特定してください。
JSONの検証:ツールとテクニック
検証は、ランタイムエラーを引き起こす前にエラーをキャッチするために重要です。以下は、さまざまな環境でJSONを検証する方法です。
コマンドライン検証
組み込みツールを使用した迅速な検証:
# Python(組み込み、どこでも利用可能)
python3 -m json.tool data.json
# 整形して検証
python3 -m json.tool data.json output.json
# jq(強力なJSONプロセッサー)
jq . data.json
# エラー詳細付きのjq
jq . data.json || echo "Invalid JSON"
# Node.jsワンライナー
node -e "console.log(JSON.parse(require('fs').readFileSync('data.json')))"
# jsonlintを使用(npmでインストール)
jsonlint data.json
プログラムによる検証
アプリケーションコードでJSONを検証:
// JavaScript/Node.js
function isValidJSON(str) {
try {
JSON.parse(str);
return true;
} catch (e) {
console.error('JSON Error:', e.message);
return false;
}
}
# Python
import json
def is_valid_json(json_str):
try:
json.loads(json_str)
return True
except json.JSONDecodeError as e:
print(f'JSON Error: {e}')
return False
// Go
import "encoding/json"
func isValidJSON(data []byte) bool {
var js json.RawMessage
return json.Unmarshal(data, &js) == nil
}
スキーマ検証
構文検証は、JSONが整形式であるかどうかのみをチェックします。スキーマ検証は、データ構造と型が要件に一致することを保証します。
JSON SchemaはJSON構造を定義するための標準です:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"age": {
"type": "integer",
"minimum": 0,
"maximum": 150
},
"email": {
"type": "string",
"format": "email"
}
},
"required": ["name", "email"]
}
人気のあるスキーマ検証ライブラリ:
- JavaScript:Ajv(最速)、joi、yup
- Python:jsonschema、pydantic
- Go:gojsonschema、jsonschema
- Java:everit-org/json-schema、networknt/json-schema-validator
プロのヒント:当社のJSONスキーマバリデーターを使用して、コードに実装する前にサンプルデータに対してスキーマをテストしてください。
オンライン検証ツール
開発中の迅速なチェック用:
- RunDev JSONバリデーター — 詳細なエラーメッセージによる即座の検証
- RunDev JSONフォーマッター — 1ステップで検証と整形
- JSONLint — 古典的なオンラインバリデーター
- JSON Schema Validator — スキーマをインタラクティブにテスト
パースとシリアライゼーションのベストプラクティス
パースはJSONテキストをネイティブデータ構造に変換します。シリアライゼーションはその逆を行います。両方の操作にはパフォーマンスとセキュリティの影響があります。
安全なパースの実践
常にJSONパースをエラーハンドリングでラップしてください:
// JavaScript - 基本的なパース
try {
const data = JSON.parse(jsonString);
// dataを使用
} catch (error) {
console.error('Failed to parse JSON:', error.message);
// エラーを適切に処理
}
// JavaScript - reviver関数を使用
const data = JSON.parse(jsonString, (key, value) => {
// 文字列から日付を変換
if (key.endsWith('Date') && typeof value === 'string') {
return new Date(value);
}
return value;
});
シリアライゼーションのベストプラクティス
オブジェクトがJSONに変換される方法を制御:
// JavaScript - 基本的なシリアライゼーション
const json = JSON.stringify(data);
// インデント付きで整形
const json = JSON.stringify(data, null, 2);
// replacerを使用したカスタムシリアライゼーション
const json = JSON.stringify(data, (key, value) => {
// 機密フィールドを削除
if (key === 'password' || key === 'apiKey') {
return undefined;
}
// BigIntを文字列に変換
if (typeof value === 'bigint'