どんな JavaScript 開発者も見たことがある:JSON.parse() が SyntaxError を投げて、アプリケーションがクラッシュ。その JSON は API、設定ファイル、ユーザのクリップボードから来た —— そしてパースできない。本ガイドは、なぜそうなるか、現場の壊れた JSON はどう見えるか、どうやって上手く対処するかを順に解説します —— シンプルな try/catch から自動修復まで。
1. なぜ JSON.parse() が失敗するか
JSON 標準(RFC 8259)は意図的に厳格です。JSON.parse() はそれを忠実に実装 —— どんな小さな逸脱でも、部分的な回復もなく SyntaxError を投げます:
try {
const data = JSON.parse('{ name: "Ada" }'); // 引用符なしキー
} catch (err) {
console.error(err.message);
// SyntaxError: Expected property name or '}' in JSON at position 2
}HTML をパースするブラウザ(エラー回復モデルが組み込まれている)と違い、JSON パーサは硬いゲートです。厳格パースは特徴:プロデューサにクリーンなデータを出すよう強制します。しかしデータソースを制御できないとき —— LLM 出力、手編集設定ファイル、サードパーティ API —— ゲートが閉まったときの戦略が必要です。
2. 壊れた JSON の最も一般的なパターン
修復ライブラリに手を伸ばす前に、相手を認識すると役立ちます。現実世界の壊れた JSON はほぼ次のいずれかに分類されます:
二重引用符の代わりに一重引用符
// ❌ 不正な JSON
{ 'name': 'Ada Lovelace' }
// ✅ 妥当
{ "name": "Ada Lovelace" }JavaScript オブジェクトリテラルは一重引用符を受け付けるが JSON は受け付けない。開発者がオブジェクトリテラルを JSON コンテキストに直接コピーし、このエラーに延々と当たる。
末尾カンマ
// ❌ 不正な JSON
{
"name": "Ada",
"active": true, // 末尾カンマ
}
// ✅ 妥当
{
"name": "Ada",
"active": true
}モダン JavaScript は配列とオブジェクトで末尾カンマを許可。JSON は許可しない。これは手編集 JSON ファイルで最も多い単一エラー。
引用符なしのオブジェクトキー
// ❌ 不正な JSON
{ name: "Ada", score: 98 }
// ✅ 妥当
{ "name": "Ada", "score": 98 }JavaScript オブジェクトリテラルは引用符付きキーを要求しない。JSON はすべてのキーを二重引用符付き文字列とする、例外なし。
JavaScript コメント
// ❌ 不正な JSON
{
// ユーザレコード
"name": "Ada",
/* 2024 追加 */
"active": true
}JSON にはコメント構文がない。コメントは設定ファイルに加えられることがある(JSONC —— コメント付き JSON —— は人気の拡張)が、JSON.parse() はそれらを拒否する。
複数行文字列
// ❌ 不正な JSON
{
"description": "line one
line two"
}
// ✅ 妥当 —— 改行をエスケープ
{
"description": "line one\nline two"
}JSON の文字列値は単一行でなければならない。文字列内のリテラル改行は禁止;代わりに \n エスケープシーケンスを使う。
NaN、Infinity、undefined
// ❌ 妥当な JSON ではない
{ "ratio": NaN, "limit": Infinity, "value": undefined }
// ✅ 妥当な代替
{ "ratio": null, "limit": 1e308, "value": null } これらは妥当な JavaScript 値だが JSON 仕様の一部ではない。JSON.stringify() は黙って変換(NaN と Infinity は null に;undefined のプロパティは丸ごと落とされる)し、微妙なラウンドトリップバグを引き起こすことがある。
Python リテラル
// ❌ Python スタイル —— 不正な JSON
{ "active": True, "deleted": False, "value": None }
// ✅ 妥当な JSON
{ "active": true, "deleted": false, "value": null } JSON は複数言語間の相互運用を念頭に設計された。Python の大文字始まりの True、False、None は言語横断データバグの頻発源。
3.「修復」と「パース」は同じ操作ではない
根本的に異なる二つの操作を区別することが大事:
- 厳格パース —— 入力が完全に正しい JSON であることを検証、任意のエラー位置を厳密に報告、逸脱を拒否。データプロデューサ を制御していて契約を強制したい場面に。
- 修復パース —— ヒューリスティック規則のセットを使い、構文的に不完全な入力から妥当な JSON 値の回復を試みる。信頼できないまたは不正確なソース(LLM 出力、ユーザのクリップボード、レガシーサービス)に。
修復パーサは仮定をする。True に遭遇したら true のつもりだったと仮定。末尾カンマに遭遇したら除去。これらの仮定は上記パターンに対してほぼ常に正しい —— しかし「修正」が意図された意味を変えてしまう、本当に不正なドキュメントを静かにマスクすることもある。
正しいツールはコンテキストに依存する。スキーマ検証された API レスポンスパイプラインでは厳格パースを望むので、悪いデータがすぐに表面化する。誰かが Slack メッセージからコピーした JSON を貼り付けている開発者ツールでは、修復パースが時間を節約する。
知っておく価値のあるライブラリ
二つの npm パッケージが重労働の大半を担い、補完的なトレードオフを持つ:
jsonrepair—— 寛容な修復パーサ。悪い JSON を渡すとベストエフォートで妥当な文字列が返る。上記パターン(一重引用符、末尾カンマ、引用符なしキー、コメント、Python リテラル、markdown フェンス、未閉じ括弧、部分入力)を扱う。同期、依存ゼロ、小さい —— メモリに完全ドキュメントがある時に理想的。clarinet—— SAX 風ストリーミング JSON パーサ。JSON がチャンクで届く時(ストリーム、大きなファイル、LLM のトークン単位レスポンス)にキー/値が現れるたびに反応したい場合に使う。構文に厳格なので、ソースが信頼できなければjsonrepairと組み合わせる。
経験則:小さく信頼できない blob があれば 修復;ドキュメントが大きいか段階的に到着するなら ストリーム。
4. エラーを捕捉してユーザに親切なフィードバックを
最小限実用なパターンは try/catch。常にこれを行う —— JSON.parse() を未捕捉で投げさせないこと:
function safeParseJson(text) {
try {
return { ok: true, value: JSON.parse(text) };
} catch (err) {
return { ok: false, error: err.message };
}
}
const result = safeParseJson(userInput);
if (!result.ok) {
showError(`Could not parse JSON: ${result.error}`);
} JSON.parse() のエラーメッセージは JavaScript エンジンによって異なる。V8(Node.js、Chrome)は位置のヒントを含む:
// V8 のエラーメッセージ
"Expected ',' or '}' after property value in JSON at position 42"
// Firefox SpiderMonkey
"JSON.parse: expected ',' or '}' after property value in object
at line 3 column 5 of the JSON data"位置情報は有用だがエンジン固有。環境横断で信頼できる行/列の報告が必要なら、独自の再帰下降パーサが構造化エラーを発行でき、ユーザに表示しやすい。
5. 自動修復するか確認を求めるか
すべての修復がリスクが同じではない。実用的なヒューリスティック:
- 自動修復が安全 —— 末尾カンマ、ホワイトスペース正規化、引用符スタイル変換、引用符なしキー、JavaScript コメント、Python の boolean/null リテラル。意味的曖昧さのない機械的変換。
- ユーザに尋ねることを検討 —— 構造的修復、たとえば未閉じオブジェクト/配列の自動クローズ(
{"name": "Ada"→{"name": "Ada"})、あるいは意図的に見えるコメントの除去。修復は多分正しいが、ユーザが確認したいかも。 - 常にフラグ、決して静かに修正しない ——
NaN → nullのような型強制はデータ値を変える。ユーザに何が変わったか見せる。
良い修復 UI は diff を表示する:左に元の入力、右に修復された出力。ユーザは結果をコピー/送信する前に修正が正しいか確認できる。
6. ブラウザ内 JSON 処理のプライバシー上の利点
ブラウザで JSON をローカル処理する —— ページを動かしている JavaScript エンジンを使う —— と、データはユーザのマシンを出ない。これは開発者がしばしば認識する以上に重要:
- API キーと資格情報 —— 開発者は秘密を含む JSON ペイロードを定期的に貼る。サーバサイドツールはあらゆるリクエストをログする。
- PII と健康データ —— データ転送がない時、GDPR と HIPAA の準拠は格段に簡単になる。
- 企業データ —— 多くのセキュリティポリシーは社内データをサードパーティ web サービスへ貼り付けることを禁ずる。
ブラウザ内処理はゼロリスクの JSON 修復を提供する:サーバなし、ログなし、データ保持なし。計算は <textarea> と数 KB の JavaScript で行われる。
よくある質問
JavaScript で JSON.parse エラーをどう処理する?
呼び出しを try/catch で包み、SyntaxError を伝播させる代わりに構造化結果を返す(上の safeParseJson ヘルパー参照)。信頼できない入力に対してガードなしで JSON.parse() を呼ばないこと。
壊れた JSON を自動でパースする方法はある?
ある —— 修復パーサがヒューリスティック(末尾カンマ除去、一重引用符変換、True の小文字化)を適用して不完全な入力から妥当な値を回復する。LLM 出力や貼り付けテキストのような信頼できないソースに使う;契約検証された API レスポンスでは悪いデータを表面化させたいので避ける。
JSON を修復するか拒否するかいつ判断?
人が近似的な JSON を貼ったり LLM が生成したりした時は修復;プロデューサを制御してスキーマを強制したい時は拒否(厳格パース)。NaN → null のような型強制は常にフラグを立て、静かに適用しない。
壊れた JSON の最も一般的な原因は?
JavaScript オブジェクトリテラルを JSON として扱うこと —— 一重引用符、引用符なしキー、末尾カンマ。JSON vs JavaScript オブジェクト と [object Object] とその他のエラー の構文エラーリファレンスを参照。
7. 今試そう —— 壊れた JSON を貼り付けて
修正が必要な不正な JSON 文字列があるなら、JSON Fix が上記のすべてのパターンを扱う —— 一重引用符、末尾カンマ、引用符なしキー、Python リテラル、コメント、markdown コードフェンス等。すべてブラウザで実行;サーバには何も送信されない。
- JSON Fix —— 壊れた JSON を貼り、即座にクリーンな JSON を取得
- オンラインで JSON を修正 —— オンライン JSON 修復ツールの仕組みと使い時
- LLM の JSON 出力を修復 —— AI 生成 JSON 修正の対象ガイド
- JSON Formatter vs JSON Repair —— どのツールをいつ使うか
- JSON Diff —— 2 つの JSON 文書を並べて比較し変化を確認