JSON との変換で何が消え、何が形を変えるのかをまとめます。undefined や Map が黙って落ちる理由、循環参照で例外になる条件、reviver と toJSON の使い方を実行して確かめます。
JSON.stringify() は値を文字列にし、JSON.parse() は文字列を値に戻します。
example.js
const data = { name: 'あ', n: 1, ok: true, list: [1, 2] };
console.log(JSON.stringify(data));
console.log(JSON.parse('{"a":1}'));
出力書き換えて実行できます
{"name":"あ","n":1,"ok":true,"list":[1,2]}
{ a: 1 }
example.js
console.log(JSON.stringify({ a: 1, b: [2] }, null, 2));
出力書き換えて実行できます
{
"a": 1,
"b": [
2
]
}
第3引数を渡すと、読みやすく字下げされます。
example.js
const data = {
u: undefined,
f() {},
s: Symbol('x'),
n: NaN,
i: Infinity,
d: new Date('2026-09-08'),
m: new Map([['k', 1]]),
};
console.log(JSON.stringify(data));
出力書き換えて実行できます
{"n":null,"i":null,"d":"2026-09-08T00:00:00.000Z","m":{}}
example.js
console.log(JSON.stringify({ a: 1, b: undefined }));
console.log(JSON.stringify([1, undefined, 2]));
出力書き換えて実行できます
{"a":1}
[1,null,2]
人が読むときだけ使ってください。 通信量が増えます。
黙って消えるもの
ここがいちばん事故になります。 例外は出ません。
example.js
console.log(JSON.stringify(undefined));
console.log(typeof JSON.stringify(undefined));
console.log(JSON.stringify(() => {}));
出力書き換えて実行できます
undefined
undefined
undefined
example.js
const data = { name: 'あ' };
data.self = data;
try {
JSON.stringify(data);
} catch (error) {
console.log(error.name);
}
try {
JSON.stringify({ n: 1n });
} catch (error) {
console.log(error.name);
}
出力書き換えて実行できます
TypeError
TypeError
| 入れたもの |
どうなるか |
undefined |
鍵ごと消える |
| 関数 |
鍵ごと消える |
Symbol |
鍵ごと消える |
NaN / Infinity |
null |
Date |
文字列になる(戻すと Date ではない) |
Map / Set |
空のオブジェクト |
JSON にはこれらを表す書き方がありません。JSON は JavaScript の一部ではないからです。
配列の中では、消えるかわりに null になります。
example.js
for (const text of ["{'a':1}", '', '{"a":1,}']) {
try {
JSON.parse(text);
} catch (error) {
console.log(error.name);
}
}
出力書き換えて実行できます
SyntaxError
SyntaxError
SyntaxError
example.js
function safeParse(text) {
try {
return { ok: true, value: JSON.parse(text) };
} catch {
return { ok: false, value: null };
}
}
console.log(safeParse('{"a":1}'));
console.log(safeParse('こわれている'));
出力書き換えて実行できます
{ ok: true, value: { a: 1 } }
{ ok: false, value: null }
配列は長さを保たないといけないので、詰められずに null が入ります。
戻り値が文字列とは限らない
example.js
console.log(JSON.parse('1'), JSON.parse('"あ"'), JSON.parse('true'), JSON.parse('null'));
console.log(JSON.stringify('あ'));
出力書き換えて実行できます
1 あ true null
"あ"
example.js
class Money {
constructor(yen) {
this.yen = yen;
}
toJSON() {
return this.yen + '円';
}
}
console.log(JSON.stringify({ price: new Money(300) }));
出力書き換えて実行できます
{"price":"300円"}
文字列ではなく undefined が返ります。
JSON.stringify(値).length のような書き方は、ここで落ちます。
例外になるもの
消えずに例外を投げるものが2つあります。
example.js
console.log(JSON.stringify({ a: 1, b: 2 }, ['a']));
console.log(JSON.stringify({ a: 1, b: 2 }, (key, value) => (key === 'b' ? undefined : value)));
出力書き換えて実行できます
{"a":1}
{"a":1}
example.js
const json = '{"date":"2026-09-08T00:00:00.000Z","n":1}';
const parsed = JSON.parse(json, (key, value) => (key === 'date' ? new Date(value) : value));
console.log(parsed.date instanceof Date);
console.log(parsed.date.toISOString());
出力書き換えて実行できます
true
2026-09-08T00:00:00.000Z
例外の文言は処理系によって違います。ここでは種類(name)だけを見ています。
読めない文字列は SyntaxError
javascript
const src = { d: new Date('2026-09-08'), u: undefined };
const copy = JSON.parse(JSON.stringify(src));
console.log(typeof copy.d);
console.log('u' in copy);
string
false
example.js
const src = { d: new Date('2026-09-08'), u: undefined };
const copy = structuredClone(src);
console.log(copy.d instanceof Date);
console.log('u' in copy);
出力書き換えて実行できます
true
true
JSON は JavaScript のオブジェクトリテラルより厳しい決まりです。
| JavaScript では書ける |
JSON では |
{ 'a': 1 } |
単引用符は不可 |
{ a: 1 } |
引用符なしの鍵は不可 |
{ "a": 1, } |
末尾のカンマは不可 |
// コメント |
コメントは不可 |
文言と読み方は、エラーのページで詳しく扱っています。
→ SyntaxError: Unexpected token
外から来た文字列は、必ず try で囲んでください。
オブジェクト以外も扱える
JSON はオブジェクトだけの形式ではありません。
文字列を stringify() すると、引用符が付いた文字列になります。
二重に変換して引用符が増えるのは、よくある間違いです。
変換のしかたを変える
toJSON() を持たせる
オブジェクト側に toJSON() があれば、その戻り値が使われます。
Date が文字列になるのも、Date が toJSON() を持っているからです。
第2引数で選ぶ・変える
配列を渡せばその鍵だけ、関数を渡せば値を書き換えられます。
undefined を返すと、その鍵は出力されません。
パスワードやトークンを外す用途に使えます。
読み込むときに戻す
JSON.parse() の第2引数(reviver)で、値を作り直せます。
よくある間違い
深い複製に使う
複製はできます。ですが中身が変わります。
Date は文字列になり、undefined の鍵は消えます。
structuredClone() を使ってください。
→ オブジェクトを結合する
鍵の順番を当てにする
JSON.stringify() は、オブジェクトの鍵の順をそのまま出します。
ですがオブジェクトの鍵の順は、整数に見える鍵が先に来ます。
文字列を比べて同じかどうか判定する用途(署名・キャッシュの鍵)では、
順番の違いで別物と判定されます。鍵を並べ替えてから変換してください。
まとめ
undefined / 関数 / Symbol は黙って消える。配列の中では null
NaN と Infinity は null。Map / Set は {}
- 循環参照と
BigInt は例外
JSON.stringify() は文字列以外を返すことがある(undefined)
- 外から来た文字列は必ず
try で囲む
- 深い複製には使わない。
structuredClone() を使う