JSON(JavaScript Object Notation)は、軽量なテキストベースのデータ交換形式であり、JavaScript のnative構文を元に設計されています。ES5より、JSONオブジェクトが導入され、データのシリアライズ・デシリアライズを扱うための標準APIが提供されました。
JSONオブジェクトは、フロットのメソッドを2つだけ持つ特殊なオブジェクトであり、コンストラクタとして使用できず、属性もありません。
typeof JSON === 'object'; // true
JSON.parse:文字列を値に復元する
JSON.parse(text, reviver?) は、JSON形式の文字列をJavaScriptの値(オブジェクト・配列・数値・文字列・真偽値・null)へ変換します。
JSON.parse('{}'); // {}
JSON.parse('true'); // true
JSON.parse('"hello"'); // "hello"
JSON.parse('123.45'); // 123.45
JSON.parse('null'); // null
引数と動作
text:パース対象のJSON文字列(または、数値・真偽値・nullで自動変換)reviver(任意):パース後の値を再帰的に加工するコールバック関数。引数は(key, value)で、返却値がそのキーに対応する実際の値になります。
JSON.parse('10', (k, v) => (k === '' ? v * 2 : v)); // 20
JSON.parse('{"a":[1,2]}', (k, v) => {
if (k === 'a') return v.map(n => n + 10);
return v;
});
// { a: [11, 12] }
加工処理の流れ:
- ネストされた構造を深さ優先で省内側から外側へwalk
- 各ノードで
reviver(key, value)を呼び出し、返された値で置換 - 最終的に
key = ''で全体構造に対する最終変換が実行される
許容されるJSONのルール
JSON.parse は、ECMA-404仕様に厳密に準拠した入力のみを受け入れます。
| 条件 | 例 | 結果 |
|---|---|---|
| 文字列はダブルクォート必須 | '"OK"' |
✅ "OK" |
'\"OK\"' |
❌ SyntaxError | |
| 数値は10進数のみ(0x, NaN等不可) | '42e1' |
✅ 420 |
'0x2A' |
❌ SyntaxError | |
:: 12.n も不可(小数点の直後に終端不可) |
'12.' |
❌ |
| 論理値・nullはリテラルで直接変換可能 | JSON.parse(true) |
❌ SyntaxError(型チェック前) |
JSON.parse('true') |
✅ true |
|
| key名はダブルクォート必須 | '{name: "T"}' |
❌ |
'{"name":"T"}' |
✅ | |
| 配列/オブジェクト末尾のカンマはNG | '[1,2,]' |
❌ |
非JSON構造(undefined, Symbol, 関数)は不可 |
JSON.parse('undefined') |
❌ |
JSON.parse('function(){}') |
❌ |
JSON.parse('{"\u0061":"A"}'); // {"a":"A"}(ユニコードエスケープは有効)
セキュリティに注意すべき代替手法(非推奨)
// ⛔ 危険!任意コード実行の可能性あり
const unsafeObj = eval('(' + jsonString + ')');
const alsoUnsafe = new Function(`return ${jsonString}`)();
JSON.stringify:値をJSON文字列に変換する
JSON.stringify(value, replacer?, space?) は、JavaScriptの値をJSON文字列にシリアライズします。
JSON.stringify({ name: "Taro" }); // '{"name":"Taro"}'
JSON.stringify([1, 2, 3]); // '[1,2,3]'
オプションパラメータの詳細
replacer- null / 非関数 / 非配列:何もしない(デフォルト動作)
- 配列:指定されたキー名のみをシリアライズ(オブジェクトのみ効果あり)
- 関数:各キーに合成処理を加えるカスタマイズ可能なフィルタ
space:整形出力用インデント指定- 数値:1~10の spaces number(例:
2→ 2つスペースでインデント) - 文字列:インデントに使用するプレフィックス(最大10文字)
null:なし(圧縮出力)
JSON.stringify(
{ a: 1, b: [2, 3] },
['a'], // bは除外
' ' // 2スペースインデント
);
// '{\n "a": 1\n}'
replacer関数の挙動
- 最初の呼び出しでは
key = '',value = 全体 - 子要素ごとに
key = プロパティ名,value = 値 - 戻り値が
undefined→ そのプロパティは省略(配列要素ではnullに置換)
JSON.stringify(
{ user: { name: 'Taro', age: 20 } },
(k, v) => typeof v === 'number' ? v + 10 : v
);
// '{"user":{"name":"Taro","age":30}}'
JSON.stringify(
{ a: 1, b: () => 0 },
(k, v) => (typeof v === 'function' ? undefined : v)
);
// '{"a":1}'(関数はキーごと削除)
変換ルールの注意点
| 型/値 | シリアライズ結果 |
|---|---|
undefined, function, Symbol(オブジェクト内) |
省略 |
undefined, function, Symbol(配列内) |
null |
NaN, Infinity, -Infinity |
"null" |
Date |
ISO文字列(例:"2024-06-10T12:34:56.789Z")※toJSON()実装済み |
BigInt |
❌ TypeError |
RegExp, Error |
"{}"(toJSON()未実装なら) |
| 循環参照 | ❌ TypeError: cyclic structure |
| プロトタイプや不可視プロパティ | 無視(enumerable:false は対象外) |
| Symbolプロパティ | 一切含まない |
数値文字列キー(例:"11")は昇順ソートされる |
{"1":a,"11":b,"foo":c}(非文字列文字列は保持) |
// カスタムtoJSONでシリアライズ方法を変更可能
const obj = { id: 1 };
obj.toJSON = () => `CustomID:${this.id}`;
JSON.stringify(obj); // '"CustomID:1"'
Unicodeとエスケープの対応
JSON.stringify({'\u0066': 1}); // '{"f":1}'
JSON.stringify('\n'); // '"\\n"'(制御文字はエスケープされる)