JSON(parse / stringify)
Node.jsでJSONを扱うには、JavaScriptに組み込まれたJSON.parse()とJSON.stringify()を使います。ファイルからのJSONの読み込みや、大きな数値・循環参照などの特殊なケースへの対処方法も合わせて解説します。
主要メソッド一覧
| メソッド / 構文 | 概要 |
|---|---|
| JSON.parse(text) | JSON文字列をJavaScriptの値(オブジェクト・配列など)に変換します。 |
| JSON.parse(text, reviver) | 各キーと値に対してreviver関数を適用しながらパースします。 |
| JSON.stringify(value) | JavaScriptの値をJSON文字列に変換します。 |
| JSON.stringify(value, replacer) | replacer関数またはキー配列を使って出力するプロパティを制御します。 |
| JSON.stringify(value, null, space) | 第3引数にスペース数(または文字列)を指定してインデントを付けて出力します。 |
| toJSON() | オブジェクトにこのメソッドを定義すると、JSON.stringify()時に呼ばれます。 |
JSON.parse / JSON.stringify の基本
JSON.parse()はJSON文字列をJavaScriptオブジェクトに変換し、JSON.stringify()はJavaScriptオブジェクトをJSON文字列に変換します。
json_basic.js
// JSON文字列をオブジェクトに変換する(JSON.parse)
var jsonStr = '{"name":"虎杖悠仁","grade":1,"cursedTechnique":"黒閃"}';
var obj = JSON.parse(jsonStr);
console.log(obj.name); // 虎杖悠仁
console.log(obj.grade); // 1
console.log(obj.cursedTechnique); // 黒閃
// オブジェクトをJSON文字列に変換する(JSON.stringify)
var sorcerer = {
name: '伏黒恵',
grade: 1,
technique: '十種影法術',
active: true,
};
// コンパクトなJSON文字列
var compact = JSON.stringify(sorcerer);
console.log(compact);
// {"name":"伏黒恵","grade":1,"technique":"十種影法術","active":true}
// インデント2スペースで整形する
var pretty = JSON.stringify(sorcerer, null, 2);
console.log(pretty);
node json_basic.js
虎杖悠仁
1
黒閃
{"name":"伏黒恵","grade":1,"technique":"十種影法術","active":true}
{
"name": "伏黒恵",
"grade": 1,
"technique": "十種影法術",
"active": true
}
reviver — パース時に値を変換する
JSON.parse()の第2引数にreviver関数を渡すと、各キーと値のペアに対して処理を適用できます。JSON文字列として格納された日付をDateオブジェクトに復元する場合などに使われます。
json_reviver.js
// 日付を文字列で格納したJSONデータ
var jsonStr = JSON.stringify({
name: '釘崎野薔薇',
enrolledAt: '2018-04-01T00:00:00.000Z',
grade: 3,
});
console.log('元のJSON:', jsonStr);
// reviverで enrolledAt キーだけ Date オブジェクトに変換する
var obj = JSON.parse(jsonStr, function(key, value) {
if (key === 'enrolledAt') {
return new Date(value); // 文字列をDateに変換する
}
return value; // それ以外はそのまま返す
});
console.log(typeof obj.enrolledAt); // object(Dateオブジェクト)
console.log(obj.enrolledAt instanceof Date); // true
console.log(obj.enrolledAt.getFullYear()); // 2018
console.log(obj.name); // 釘崎野薔薇
node json_reviver.js
元のJSON: {"name":"釘崎野薔薇","enrolledAt":"2018-04-01T00:00:00.000Z","grade":3}
object
true
2018
釘崎野薔薇
replacer — 出力するプロパティを制御する
JSON.stringify()の第2引数にreplacer関数またはキー名の配列を渡すと、出力するプロパティを絞り込んだり、値を変換したりできます。
json_replacer.js
var sorcerer = {
name: '五条悟',
grade: 'special',
technique: '無下限呪術',
secretInfo: '六眼の詳細データ', // 外部に出したくない情報
power: 99999,
};
// replacer に配列を渡して出力するキーを限定する
var limited = JSON.stringify(sorcerer, ['name', 'grade', 'technique'], 2);
console.log('配列replacer:');
console.log(limited);
// replacer に関数を渡して値を変換する
var masked = JSON.stringify(sorcerer, function(key, value) {
if (key === 'secretInfo') {
return undefined; // undefinedを返すとそのキーを除外する
}
if (key === 'power' && value > 10000) {
return '計測不能';
}
return value;
}, 2);
console.log('関数replacer:');
console.log(masked);
node json_replacer.js
配列replacer:
{
"name": "五条悟",
"grade": "special",
"technique": "無下限呪術"
}
関数replacer:
{
"name": "五条悟",
"grade": "special",
"technique": "無下限呪術",
"power": "計測不能"
}
循環参照の対策
オブジェクトが自分自身を参照している(循環参照)場合、JSON.stringify()はTypeErrorをスローします。replacer関数でWeakSetを使って検出するか、ライブラリを使う方法があります。
json_circular.js
// 循環参照のあるオブジェクトを作る
var curse = { name: '両面宿儺', rank: 'special' };
curse.self = curse; // 自分自身を参照する(循環参照)
// そのままstringifyするとエラーになる
try {
JSON.stringify(curse);
} catch (e) {
console.log('エラー:', e.message);
// TypeError: Converting circular structure to JSON
}
// WeakSetを使って循環参照を検出するreplacerを作る
function safeStringify(obj, space) {
var seen = new WeakSet();
return JSON.stringify(obj, function(key, value) {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular]'; // 循環参照は文字列に置き換える
}
seen.add(value);
}
return value;
}, space);
}
var result = safeStringify(curse, 2);
console.log(result);
node json_circular.js
エラー: Converting circular structure to JSON
{
"name": "両面宿儺",
"rank": "special",
"self": "[Circular]"
}
BigInt のシリアライズ
JavaScriptのBigInt型はデフォルトではJSON.stringify()できません。toJSON()メソッドを追加するか、replacerで文字列に変換する方法で対処します。
json_bigint.js
// BigIntをそのままstringifyするとエラーになる
try {
JSON.stringify({ cursedEnergy: 9007199254740993n });
} catch (e) {
console.log('エラー:', e.message);
// TypeError: Do not know how to serialize a BigInt
}
// replacerでBigIntを文字列に変換する
function bigIntReplacer(key, value) {
if (typeof value === 'bigint') {
return value.toString(); // 文字列として出力する
}
return value;
}
var data = {
name: '虎杖悠仁',
cursedEnergy: 9007199254740993n, // Number型の安全な整数範囲を超える値
};
var json = JSON.stringify(data, bigIntReplacer, 2);
console.log(json);
// パース時は文字列のままになる(BigIntに戻す場合はreviverを使う)
var parsed = JSON.parse(json, function(key, value) {
if (key === 'cursedEnergy') {
return BigInt(value); // 文字列をBigIntに戻す
}
return value;
});
console.log(typeof parsed.cursedEnergy); // bigint
console.log(parsed.cursedEnergy); // 9007199254740993n
node json_bigint.js
エラー: Do not know how to serialize a BigInt
{
"name": "虎杖悠仁",
"cursedEnergy": "9007199254740993"
}
bigint
9007199254740993n
ファイルからJSON を読み込む
Node.jsでJSONファイルを読み込むには、fs.readFileSync()で文字列として読み込んでからJSON.parse()する方法と、require()で直接読み込む方法があります。
sorcerers.json
[
{ "name": "虎杖悠仁", "grade": 1, "technique": "黒閃" },
{ "name": "伏黒恵", "grade": 1, "technique": "十種影法術" },
{ "name": "釘崎野薔薇","grade": 3, "technique": "芻霊呪法" },
{ "name": "五条悟", "grade": "special","technique": "無下限呪術" }
]
json_file.js
var fs = require('fs');
var path = require('path');
// 方法1: fs.readFileSync + JSON.parse(エンコードを明示できる)
var filePath = path.join(__dirname, 'sorcerers.json');
var raw = fs.readFileSync(filePath, 'utf8');
var sorcerers = JSON.parse(raw);
console.log('readFileSync方式:');
sorcerers.forEach(function(s) {
console.log(s.name + ' [' + s.grade + '級]');
});
// 方法2: require()で読み込む(Node.jsがJSONを自動パースする)
// ※ requireはモジュールをキャッシュするため、動的な再読み込みには向かない
var cached = require('./sorcerers.json');
console.log('\nrequire方式:');
console.log(cached[0].name); // 虎杖悠仁
// JSONファイルに書き込む
var newData = { name: '両面宿儺', grade: 'special', technique: '斬撃' };
fs.writeFileSync(
path.join(__dirname, 'ryomen.json'),
JSON.stringify(newData, null, 2),
'utf8'
);
console.log('\nファイル書き込み完了');
node json_file.js readFileSync方式: 虎杖悠仁 [1級] 伏黒恵 [1級] 釘崎野薔薇 [3級] 五条悟 [special級] require方式: 虎杖悠仁 ファイル書き込み完了
概要
JSON.parse()とJSON.stringify()はJavaScript組み込みのメソッドであり、Node.jsでもそのまま使えます。第2引数のreviver・replacer関数を活用すると、日付の復元や機密フィールドの除外など、柔軟な変換処理を組み込めます。
循環参照はWeakSetで検出できます。BigInt型はデフォルトではシリアライズできないため、replacerで文字列に変換してから保存し、reviverで復元するパターンが一般的です。
JSONファイルの読み込みはfs.readFileSync()+JSON.parse()が基本です。require()でも読み込めますが、モジュールキャッシュが効くため、ファイルを動的に再読み込みしたい場合はfs.readFileSync()を使います。
よくあるミス
try-catch なしで JSON.parse() してクラッシュする
JSON.parse() は不正なJSON文字列を受け取ると SyntaxError をスローします。外部からのデータ(APIレスポンス・ファイル読み込みなど)をパースする場合、try-catch なしで呼び出すとプログラム全体がクラッシュします。
NGの書き方(try-catch がない):
// NG: 不正なJSON文字列を受け取るとそのままクラッシュする
var data = JSON.parse('{"name": 虎杖悠仁}'); // 値がクォートされていない
console.log(data.name);
node ng.js
SyntaxError: Unexpected token '虎', "{"name": 虎杖悠仁}" is not valid JSON
OKの書き方(try-catch でエラーを捕捉する):
// OK: try-catch で SyntaxError を捕捉する
try {
var data = JSON.parse('{"name": 虎杖悠仁}');
console.log(data.name);
} catch (e) {
console.error('JSONのパースに失敗しました:', e.message);
}
node ok.js
JSONのパースに失敗しました: Unexpected token '虎', "{"name": 虎杖悠仁}" is not valid JSON
外部から受け取るデータは常にtry-catchで囲む習慣をつけることで、予期せぬクラッシュを防げます。
undefined・関数・Symbol が JSON.stringify() で消える
JSON.stringify() はJSONで表現できない型(undefined・関数・Symbol)をオブジェクトのプロパティとして持っている場合、そのプロパティを出力から無警告で除外します。データが消えてもエラーにならないため、見落としやすいミスです。
var sorcerer = {
name: '五条悟',
technique: undefined, // undefined → 除外される
activate: function() { return 1; }, // 関数 → 除外される
id: Symbol('gojo'), // Symbol → 除外される
grade: 'special',
};
var json = JSON.stringify(sorcerer, null, 2);
console.log(json);
node symbol_test.js
{
"name": "五条悟",
"grade": "special"
}
technique・activate・id の3つのプロパティはすべて出力から消えています。意図せずデータが欠落しないよう、保存前にシリアライズ結果を確認するか、replacerで明示的に処理することが重要です。配列の要素が undefined や関数の場合は除外ではなく null に変換されます。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。