JavaScript でオブジェクトや配列を扱っていると、「サーバーに送るために文字列にしたい」「localStorage に保存したい」「逆に受け取った文字列をオブジェクトに戻したい」という場面に必ず出会います。そのときに使うのが JSON.stringify() と JSON.parse() です。この記事では、オブジェクト・配列を JSON 文字列へ変換する JSON.stringify() と、JSON 文字列をオブジェクト・配列へ戻す JSON.parse() の基本から、整形(インデント)する方法、fetch や localStorage と組み合わせた実践例、そして parse の例外処理や stringify が無視する値など、つまずきやすいポイントまで順に解説します。
目次
JSON.parse と JSON.stringify が必要な理由
JSON(JavaScript Object Notation)は、データを表現するためのテキスト形式です。見た目は JavaScript のオブジェクトや配列によく似ていますが、あくまで「文字列」です。プログラム内で扱うオブジェクトと、ネットワークや保存先でやり取りする文字列は別物なので、その間を変換する仕組みが必要になります。その橋渡しをするのが、この2つのメソッドです。
| メソッド | 変換の向き |
|---|---|
JSON.stringify() | オブジェクト・配列 → JSON 文字列 |
JSON.parse() | JSON 文字列 → オブジェクト・配列 |
たとえば、サーバーとの API 通信ではデータを文字列としてやり取りするのが基本です。送るときはオブジェクトを JSON.stringify() で文字列にし、受け取った文字列は JSON.parse() でオブジェクトに戻します。また、ブラウザの localStorage は文字列しか保存できないため、オブジェクトをそのまま保存することはできません。一度 JSON.stringify() で文字列に変換してから保存し、読み出すときに JSON.parse() で元に戻す、という使い方をします。
基本の使い方
まずは JSON.stringify() の最小の例です。オブジェクトを渡すと、それを表す JSON 文字列が返ってきます。
const user = { name: "Taro", age: 28, isAdmin: false };
// オブジェクト → JSON 文字列
const json = JSON.stringify(user);
console.log(json);
// {"name":"Taro","age":28,"isAdmin":false}
console.log(typeof json);
// string
注目したいのは、戻り値が文字列(string)になっている点と、JSON ではプロパティ名(キー)が必ずダブルクォートで囲まれる点です。配列も同じように文字列へ変換できます。
反対に、JSON 文字列をオブジェクトに戻すのが JSON.parse() です。先ほどの文字列を渡すと、元のようなオブジェクトが返ってきます。
const json = '{"name":"Taro","age":28,"isAdmin":false}';
// JSON 文字列 → オブジェクト
const user = JSON.parse(json);
console.log(user.name); // Taro
console.log(user.age); // 28
console.log(typeof user); // object
JSON.parse() で戻したあとは、通常のオブジェクトと同じように user.name のようにドット記法でプロパティへアクセスできます。この「文字列にする」「文字列から戻す」が、2つのメソッドの基本動作です。
JSON.stringify の第2引数・第3引数
JSON.stringify() は、実は3つの引数を受け取れます。構文は JSON.stringify(value, replacer, space) です。それぞれの役割を整理すると次のようになります。
| 引数 | 役割 |
|---|---|
value | 文字列に変換したいオブジェクトや配列、値 |
replacer | 出力するプロパティを絞り込んだり、値を加工したりする(関数または配列)。省略可 |
space | インデントの幅(数値)か、インデントに使う文字列。整形に使う。省略可 |
第3引数 space で読みやすく整形する
第3引数 space に数値を渡すと、その数だけスペースでインデントした、人間が読みやすい形に整形してくれます。デバッグでオブジェクトの中身を確認したいときや、ファイルに見やすく書き出したいときに便利です。
const user = { name: "Taro", age: 28, hobbies: ["coding", "music"] };
// 第3引数に 2 を渡すと、スペース2つでインデントされる
console.log(JSON.stringify(user, null, 2));
/*
{
"name": "Taro",
"age": 28,
"hobbies": [
"coding",
"music"
]
}
*/
第2引数 replacer を使わないときは、上の例のように null を渡します。space には数値のほか、"\t"(タブ)のような文字列を指定することもできます。
第2引数 replacer で出力を絞り込む
第2引数 replacer に文字列の配列を渡すと、その名前のプロパティだけを出力できます。パスワードのような出力したくない項目を除外したいときなどに使えます。
const user = { name: "Taro", age: 28, password: "secret" };
// name と age だけを出力する
console.log(JSON.stringify(user, ["name", "age"]));
// {"name":"Taro","age":28}
普段の開発では第2引数まで使う場面はそれほど多くありませんが、「整形のための第3引数を使うときは、第2引数に null を置く必要がある」という点だけ覚えておくと困りません。
fetch のレスポンスを JSON で扱う
API からデータを取得する fetch() では、JSON との変換が頻繁に登場します。サーバーから返ってくるのは文字列なので、それをオブジェクトに変換して初めて中身を扱えます。fetch() のレスポンスには response.json() という便利なメソッドがあり、これは内部的に本文を読み取って JSON.parse() 相当の変換を行ってくれます。
async function getUser() {
const response = await fetch("https://example.com/api/user/1");
// response.json() が JSON 文字列をオブジェクトへ変換してくれる
const user = await response.json();
console.log(user.name);
}
getUser();
逆に、データを送信するときは自分で JSON.stringify() を使います。fetch() の body には文字列を渡す必要があるため、オブジェクトをそのまま渡すのではなく、JSON 文字列に変換してから送ります。あわせて、サーバーに「JSON を送っている」と伝えるために Content-Type ヘッダーを指定します。
const newUser = { name: "Hanako", age: 24 };
await fetch("https://example.com/api/user", {
method: "POST",
headers: { "Content-Type": "application/json" },
// オブジェクトを JSON 文字列にして送る
body: JSON.stringify(newUser),
});
localStorage にオブジェクトを保存・復元する
ブラウザの localStorage は、キーと値のペアでデータを保存できる仕組みですが、値として保存できるのは文字列だけです。オブジェクトをそのまま setItem() に渡すと、"[object Object]" という意味のない文字列になってしまいます。そこで、保存するときに JSON.stringify() で文字列化し、取り出すときに JSON.parse() でオブジェクトへ戻す、というやり方が定番です。
const settings = { theme: "dark", fontSize: 16 };
// 保存:オブジェクトを JSON 文字列にしてから保存する
localStorage.setItem("settings", JSON.stringify(settings));
// 復元:取り出した文字列をオブジェクトへ戻す
const saved = JSON.parse(localStorage.getItem("settings"));
console.log(saved.theme); // dark
なお、保存したキーがまだ存在しない場合、getItem() は null を返します。JSON.parse(null) はエラーにはならず null を返しますが、その後にプロパティへアクセスするとエラーになるので、後述の例外処理や初期値の用意とあわせて扱うと安心です。
JSON.parse(JSON.stringify()) でディープコピーする
オブジェクトを丸ごと複製したいとき、JSON.parse(JSON.stringify(obj)) という書き方がよく使われます。一度 JSON 文字列にしてから新しいオブジェクトとして読み直すため、ネストした中身まで含めて独立したコピー(ディープコピー)が作れます。次の例では、コピー側を書き換えても元のオブジェクトには影響していないことが分かります。
const original = { user: { name: "Taro" }, tags: ["a", "b"] };
// JSON 文字列を経由してディープコピーを作る
const copy = JSON.parse(JSON.stringify(original));
copy.user.name = "Jiro";
console.log(copy.user.name); // Jiro
console.log(original.user.name); // Taro(元は変わっていない)
手軽で覚えやすい方法ですが、この方法には限界があります。後述するとおり、関数や undefined、Date オブジェクトなどは正しくコピーされません。それらを含まない単純なデータであれば問題なく使えますが、確実にコピーしたい場合は、現在のブラウザに用意されている structuredClone() を使うのが安全です。
不正な JSON 文字列で JSON.parse が例外になる
JSON.parse() は、渡された文字列が正しい JSON 形式でないと SyntaxError という例外を投げます。たとえばプロパティ名がダブルクォートで囲まれていなかったり、末尾に余分なカンマがあったりするだけで失敗します。API から空のレスポンスが返ってきたときや、ユーザー入力をそのまま parse したときなど、想定外の文字列が来る可能性がある場面では、対策しておかないとそこでスクリプト全体が止まってしまいます。
こうした場面では try...catch で囲み、失敗したときの処理を用意しておきます。次の例では、変換に失敗した場合に null を返すようにしています。
function safeParse(text) {
try {
return JSON.parse(text);
} catch (error) {
// 不正な JSON のときはここに来る
console.error("JSON の解析に失敗しました:", error.message);
return null;
}
}
console.log(safeParse('{"name":"Taro"}')); // { name: 'Taro' }
console.log(safeParse("{name:'Taro'}")); // null(不正な JSON)
JSON.stringify が無視する値と循環参照
JSON.stringify() は、JavaScript の値をすべてそのまま文字列にできるわけではありません。JSON という形式で表現できない値は、無視されたり、別の値に置き換えられたりします。意図せずデータが欠けてしまうことがあるので、挙動を知っておくと安心です。
undefined・関数・Symbol は無視される
オブジェクトのプロパティの値が undefined・関数・Symbol の場合、そのプロパティは出力から丸ごと取り除かれます。配列の中にこれらがあった場合は、取り除かれるのではなく null に置き換えられます。
const data = {
name: "Taro",
greet: function () {}, // 関数 → 無視される
age: undefined, // undefined → 無視される
};
console.log(JSON.stringify(data));
// {"name":"Taro"}
// 配列の中では null に置き換えられる
console.log(JSON.stringify([1, undefined, function () {}, 4]));
// [1,null,null,4]
循環参照があるとエラーになる
オブジェクト同士が互いを参照し合っている(自分自身を含んでいる)状態を循環参照といいます。JSON.stringify() は循環参照を文字列で表現できないため、TypeError の例外を投げます。DOM 要素などは内部に循環参照を持っていることがあるので、そのまま stringify しようとすると失敗します。
const obj = { name: "Taro" };
obj.self = obj; // 自分自身を参照させる(循環参照)
JSON.stringify(obj);
// TypeError: Converting circular structure to JSON
Date は文字列になり、parse しても Date に戻らない
Date オブジェクトは、JSON.stringify() によって ISO 形式の文字列に変換されます。問題はその後で、JSON.parse() はその文字列を Date オブジェクトには戻さず、ただの文字列のまま返します。日付として再び扱いたい場合は、parse した後に自分で new Date() で変換し直す必要があります。
const data = { createdAt: new Date("2026-06-10T00:00:00Z") };
const json = JSON.stringify(data);
console.log(json);
// {"createdAt":"2026-06-10T00:00:00.000Z"}
const restored = JSON.parse(json);
console.log(typeof restored.createdAt); // string(Date ではない)
// Date として使いたいときは自分で変換する
const date = new Date(restored.createdAt);
console.log(date.getFullYear()); // 2026
まとめ
JSON.stringify() はオブジェクトや配列を JSON 文字列に変換し、JSON.parse() はその逆に JSON 文字列をオブジェクトや配列へ戻すメソッドです。API とのデータ送受信や、文字列しか保存できない localStorage へのオブジェクト保存など、活躍する場面はたくさんあります。JSON.stringify(value, replacer, space) の第3引数 space を使えば読みやすく整形でき、第2引数 replacer で出力するプロパティを絞り込むこともできます。
一方で、JSON.parse() は不正な文字列で例外を投げるため try...catch が欠かせず、JSON.stringify() は undefined・関数・Symbol を無視し、循環参照ではエラーになります。また Date は文字列化されて parse では戻らない点にも注意が必要です。これらの特性を理解しておけば、ディープコピーの簡易テクニックも含め、JSON 変換を安心して使いこなせるようになります。