妥当そうに見えるデータを JSON パーサに貼ると、即座に SyntaxError: Unexpected token ''' が投げられる。そのデータは JavaScript のソースコード由来で完全に妥当に見える —— しかし JSON と JavaScript のオブジェクトリテラルは同じフォーマットではなく、その差は多くの開発者が意識する以上に重要です。
短い答え:JSON は一重引用符をサポートしない
JSON では一重引用符は完全に不正。すべての文字列 —— キーも値も —— は二重引用符を使わなければなりません。例外はありません。
// ❌ 不正な JSON —— 一重引用符の文字列
{ 'name': 'Ada Lovelace', 'active': true }
// ✅ 妥当な JSON
{ "name": "Ada Lovelace", "active": true }各環境で見るエラー:
// Chrome / Node.js (V8)
SyntaxError: Unexpected token "'", "{'name':..." is not valid JSON
// Firefox
SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data
// Safari
SyntaxError: JSON Parse error: Single quotes (') are not allowed in JSONSafari のメッセージが最も親切 —— 問題が何かを直接伝えてくれます。他は予期しない文字を報告するだけ。
なぜ JSON は二重引用符を要求するのか?
JSON は 2001 年に Douglas Crockford によって定義され、RFC 8259 として標準化されました。文法はちょうど一つの文字列区切り、二重引用符(U+0022)を指定。一重引用符は文法にそもそも存在しません。
理由はシンプルさ。JSON は JavaScript だけでなく、どの言語からもパースされることを意図したデータ交換フォーマットです。引用符スタイルを一つに強制することで、曖昧さの一クラスを丸ごと排除。Go、Python、Java、Rust など他のすべての言語のパーサがまったく同じ規則を実装します。
JSON vs JavaScript オブジェクトリテラル:完全な差分
一重引用符は数多くの違いの一つに過ぎません。全体像:
1. キーは二重引用符付き文字列でなければならない
// ❌ JavaScript オブジェクトリテラル —— 裸のキー、一重引用符キー
{ name: "Ada", 'score': 98 }
// ✅ JSON —— すべてのキーは二重引用符付き文字列
{ "name": "Ada", "score": 98 }JavaScript オブジェクトリテラルは裸の識別子、一重引用符文字列、二重引用符文字列をキーとして受け付けます。JSON は二重引用符文字列のみ。
2. 末尾カンマ不可
// ❌ 不正な JSON
{ "name": "Ada", "score": 98, }
// ^ 末尾カンマ
// ✅ 妥当な JSON
{ "name": "Ada", "score": 98 }モダン JavaScript は明示的に末尾カンマを許可。JSON は不可 —— JSON の末尾カンマに関する完全ガイドを参照。
3. コメントなし
// ❌ 不正な JSON
{
// ユーザプロファイル
"name": "Ada"
}
// ✅ JSON にコメント構文はない
{ "name": "Ada" }JSON にはコメント構文がまったくありません。設定ファイルでコメントが必要なら、VS Code 設定、TypeScript の tsconfig.json、いくつかの設定ファイルパーサがサポートする JSONC 形式(JSON-with-Comments)を検討。
4. undefined、関数、Symbol なし
// JavaScript オブジェクト —— シリアライズ不能な値を受け付ける
const obj = {
fn: () => 42, // 関数
sym: Symbol('id'), // Symbol
val: undefined, // undefined
};
// JSON.stringify はそれらを黙って落とすか変換
JSON.stringify(obj);
// → '{}' (3 つのプロパティが消えるか null になる) JSON が対応する値型はちょうど 6 つ:string、number、boolean(true/false)、null、object、array。関数、Symbol、undefined に JSON 表現はありません。JSON.stringify() はそれらの値を持つオブジェクトプロパティを黙って落とし、配列内では null に変換します。
5. NaN や Infinity なし
JSON.stringify({ ratio: NaN, limit: Infinity });
// → '{"ratio":null,"limit":null}'
// NaN と Infinity は null になる —— 黙って!
JSON.parse('{"ratio": NaN}');
// → SyntaxError: Unexpected token 'N'NaN と Infinity は妥当な JavaScript 数値ですが、JSON の数値仕様の一部ではありません。JSON.stringify() は出る時に null に変換、JSON.parse() は入る時に拒否。この非対称性は微妙なバグの一般的な原因。
6. 数値:16 進数なし、先頭ゼロなし、+ なし
// ❌ 不正な JSON
{ "flags": 0xFF, "code": 007, "delta": +1.5 }
// ✅ 妥当な JSON
{ "flags": 255, "code": 7, "delta": 1.5 } JSON の数値は 10 進で、明示的な + 符号で始められず、先頭ゼロもダメ(小数点前の単一桁 0 を除く)。16 進数や 8 進数のリテラルは妥当な JSON ではありません。
7. 複数行文字列なし
// ❌ 不正な JSON —— 文字列内のリテラル改行
{ "bio": "Line one
Line two" }
// ✅ エスケープシーケンスを使う
{ "bio": "Line one\nLine two" }8. 計算キー、Symbol キー、メソッド省略記法
JSON のように見えて相当物がない JS 専用の機能をさらに 3 つ:
// ❌ 計算プロパティ名 —— 評価される、リテラルではない
const k = "id";
const obj = { [k]: 1, [`user_${k}`]: 1 };
// JSON に式はない;キーはリテラルな二重引用符文字列でなければならない。
// ❌ Symbol キー —— JSON.stringify に黙って落とされる
const tag = Symbol('tag');
JSON.stringify({ [tag]: 'admin' }); // → '{}'
// ❌ メソッド省略記法 —— 関数は落とされる/省略される
const obj = { greet() { return 'hi'; } };
JSON.stringify(obj); // → '{}' これらのいずれかが必要なら、オブジェクトの JSON 形状の射影をシリアライズしましょう —— JSON.stringify() は常に文字列キーと 6 つの JSON 値型しか生み出しません。
クイック変換チートシート
| JavaScript オブジェクトリテラル | 妥当な JSON 同等物 |
|---|---|
{ name: "Ada" } | {"name":"Ada"} |
{ 'name': 'Ada' } | {"name":"Ada"} |
{ a: 1, } | {"a":1} |
{ val: undefined } | {}(プロパティ脱落) |
{ n: NaN } | {"n":null} |
{ n: Infinity } | {"n":null} |
{ fn: () => 1 } | {}(プロパティ脱落) |
0xFF | 255 |
JS オブジェクトを JSON に変換する最も安全な方法
引用符を手作業で書き換えて JavaScript オブジェクトを JSON に変換しないでください。JSON.stringify() を使えば、すべての境界条件を正しく扱います:
const obj = { name: 'Ada', score: 98, active: true };
// コンパクト
JSON.stringify(obj);
// → '{"name":"Ada","score":98,"active":true}'
// 2 スペースインデントで pretty-print
JSON.stringify(obj, null, 2);
// → '{
"name": "Ada",
"score": 98,
"active": true
}' JSON.stringify() の第二引数は replacer —— キー名の配列を渡してそれらのプロパティのみを含めたり、関数を渡して値を変換できます:
// 特定のキーのみ含める
JSON.stringify(obj, ['name', 'score'], 2);
// → '{
"name": "Ada",
"score": 98
}'
// カスタム replacer —— undefined を null に変換
JSON.stringify({ a: 1, b: undefined }, (key, value) =>
value === undefined ? null : value
);
// → '{"a":1,"b":null}'JS オブジェクトリテラルが文字列で来た場合
JSON のように見えて実は JavaScript オブジェクトリテラルなデータを受け取ることがあります —— ログファイル、デバッグツール、仕様に従っていない API などから。その場合 JSON.parse() は失敗し、選択肢は二つ:
- 修復パーサを使う —— 一重引用符を二重引用符に変換、末尾カンマを除去、裸キーに引用符を付与、その他のすべての違いを自動で処理。信頼できない入力には最も安全。
eval()またはFunction()を使う —— 妥当な JavaScript には技術的に動作しますが、ソースが完全に信頼できない限り深刻なセキュリティリスク。ユーザ提供入力では絶対にやらないこと。
よくある質問
JSON は一重引用符をサポート?
いいえ。JSON 文法(RFC 8259)はちょうど一つの文字列区切り —— 二重引用符 —— を定義。キーも値も二重引用符を使わなければならず、一重引用符は SyntaxError を投げます。
JSON と JavaScript オブジェクトの違いは?
JSON は厳格なテキスト形式、JavaScript オブジェクトリテラルは寛容なコード。JS は一重引用符、引用符なしキー、末尾カンマ、コメント、undefined、NaN、関数、16 進数を許す —— どれも妥当な JSON ではありません。完全なチートシートは上を、入門は JSON とは?
JavaScript オブジェクトを JSON に変換するには?
JSON.stringify() を使う —— 引用符を手で編集しない。すべての境界条件を扱い、シリアライズ不能な値を落とし、文字列を正しくエスケープします。
なぜ eval() で JS オブジェクトリテラルをパースできない?
eval() は任意コードを実行するため、信頼できない入力には深刻なセキュリティリスク。代わりに修復パーサ(または JSON Fix)を使って JS オブジェクトリテラルを安全に妥当な JSON に変換しましょう。
一重引用符を即修正
今すぐ妥当な JSON に変換したい JavaScript オブジェクトリテラルがあるなら、JSON Fix が変換を自動で行います。一重引用符を二重に変換、裸キーに引用符を付け、末尾カンマを取り、上記の他のすべての差分を修復 —— ブラウザ内で、サーバには何も送られません。
- JSON Fix —— JS オブジェクトリテラルを即時に妥当な JSON へ変換
- JSON の一重引用符を直す —— 壊れた例と直した例つきのクイックリファレンス
- JSON vs JavaScript オブジェクトリテラル —— 各構文の違いと並列の例
- JSON.parse Unexpected Token エラー —— SyntaxError の各バリエーションのガイド
- JSON の末尾カンマ —— JS で許され JSON で禁じられる理由