JSON.stringify()

JSON.stringify() メソッドは、ある JavaScript のオブジェクトや値を JSON 文字列に変換します。置き換え関数を指定して値を置き換えたり、置き換え配列を指定して指定されたプロパティのみを含むようにしたりすることもできます。

構文

JSON.stringify(value[, replacer[, space]])

引数

value
JSON 文字列に変換する値です。
replacer Optional
文字列化の手順の挙動を変更する関数、または値のオブジェクトを JSON 文字列に含めるプロパティを選択するホワイトリストとして機能する StringNumber オブジェクトの配列。もしこの値が null であるか提供されなかった場合は、結果の文字列にオブジェクトのすべてのプロパティが含まれます。
space Optional
出力する JSON 文字列に可読性を目的に空白を挿入するために使う String または Number オブジェクトです。

これが Number のときは、空白として使う空白文字の数を示します。この数の上限は10です (それより大きい数値は、単に 10 となります)。 1 より小さい値は空白を使わないことを示します。

これが String のときは、その文字列 (10文字より長い場合はその最初の10文字) が空白として使われます。もしこの引数が提供されない (または null である) 場合は、空白は使用されません。

返値

与えられた値を表現する JSON 文字列。

例外

  • TypeError ("cyclic object value") 例外は、循環参照が見つかった時に発生します。
  • TypeError ("BigInt value can't be serialized in JSON") 例外は、 BigInt 値を文字列化しようとしたときに発生します。

説明

JSON.stringify() は値をそれを表す JSON 表記に変換します。

  • 値が toJSON() メソッドを持っている場合は、データがどのようにシリアライズされるかを定義する必要があります。
  • BooleanNumberString の各オブジェクトは、文字列化の際に慣習的な変換セマンティクスに従い、対応するプリミティブ値に変換されます。
  • 変換の際に undefined、 関数 (Function)、シンボル (Symbol) は有効な JSON 値ではありません。変換中にそのような値に遭遇した場合は、 (オブジェクトの中で発見された場合は) 省略されたり、 (配列の中で見つかった場合は) null に変換されたりします。 JSON.stringifyJSON.stringify(function(){})JSON.stringify(undefined) のように「純粋」な値を渡した場合に undefined を返すことがあります。
  • シンボル (Symbol) がキーとなっているプロパティはすべて、 replacer 関数を使用する場合でも完全に無視されます。
  • Date のインスタンスは文字列を返す toJSON() を実装しています (date.toISOString() と同じです)。したがって、これらは文字列として扱われます。
  • Infinity および NaN の数値は、 null の値と同様、すべて null と見なされます。
  • その他のすべての Object のインスタンスは (Map, Set, WeakMap, WeakSet を含め)、列挙可能なプロパティのみがシリアライズされます。
JSON.stringify({});                    // '{}'
JSON.stringify(true);                  // 'true'
JSON.stringify('foo');                 // '"foo"'
JSON.stringify([1, 'false', false]);   // '[1,"false",false]'
JSON.stringify([NaN, null, Infinity]); // '[null,null,null]'
JSON.stringify({ x: 5 });              // '{"x":5}'

JSON.stringify(new Date(2006, 0, 2, 15, 4, 5)) 
// '"2006-01-02T15:04:05.000Z"'

JSON.stringify({ x: 5, y: 6 });
// '{"x":5,"y":6}'
JSON.stringify([new Number(3), new String('false'), new Boolean(false)]);
// '[3,"false",false]'

// 文字列がキーとなった配列要素は列挙可能ではなく、 JSON では意味をなさない
let a = ['foo', 'bar'];
a['baz'] = 'quux';      // a: [ 0: 'foo', 1: 'bar', baz: 'quux' ]
JSON.stringify(a); 
// '["foo","bar"]'

JSON.stringify({ x: [10, undefined, function(){}, Symbol('')] }); 
// '{"x":[10,null,null,null]}' 

// 標準データ構造
JSON.stringify([new Set([1]), new Map([[1, 2]]), new WeakSet([{a: 1}]), new WeakMap([[{a: 1}, 2]])]);
// '[{},{},{},{}]'

// 型付き配列
JSON.stringify([new Int8Array([1]), new Int16Array([1]), new Int32Array([1])]);
// '[{"0":1},{"0":1},{"0":1}]'
JSON.stringify([new Uint8Array([1]), new Uint8ClampedArray([1]), new Uint16Array([1]), new Uint32Array([1])]);
// '[{"0":1},{"0":1},{"0":1},{"0":1}]'
JSON.stringify([new Float32Array([1]), new Float64Array([1])]);
// '[{"0":1},{"0":1}]'
 
// toJSON()
JSON.stringify({ x: 5, y: 6, toJSON(){ return this.x + this.y; } });
// '11'

// シンボル:
JSON.stringify({ x: undefined, y: Object, z: Symbol('') });
// '{}'
JSON.stringify({ [Symbol('foo')]: 'foo' });
// '{}'
JSON.stringify({ [Symbol.for('foo')]: 'foo' }, [Symbol.for('foo')]);
// '{}'
JSON.stringify({ [Symbol.for('foo')]: 'foo' }, function(k, v) {
  if (typeof k === 'symbol') {
    return 'a symbol';
  }
});
// undefined

// enumerable でないプロパティ:
JSON.stringify( Object.create(null, { x: { value: 'x', enumerable: false }, y: { value: 'y', enumerable: true } }) );
// '{"y":"y"}'


// BigInt の値は例外が発生
JSON.stringify({x: 2n});
// TypeError: BigInt value can't be serialized in JSON

replacer 引数

replacer 引数は関数または配列です。

関数については、キーと文字列化される値の 2 つの引数を取ります。キーをもつオブジェクトが replacerthis パラメータとして提供されます。

最初に文字列化されるオブジェクトを表す空のキーで呼ばれ、それからそれぞれの文字列化されるオブジェクトや配列のプロパティについて呼ばれます。

JSON 文字列に加える値を、次のように返します。

  • Number を返すと、 JSON 文字列に追加されるときにその数値に対応する文字列がプロパティの値として使用されます。
  • String を返すと、 JSON 文字列に追加されるときにその文字列がプロパティの値として使用されます。
  • Boolean を返すと、 JSON 文字列に追加されるときに"true" または "false" が適切にプロパティの値として使用されます。
  • null を返すと、 null が JSON 文字列に追加されます。
  • その他のオブジェクトを返した場合、そのオブジェクトは再帰的に JSON 文字列に文字列化されます。その時それぞれのプロパティに対して、そのオブジェクトが関数である場合を除いて replacer 関数を呼びます。関数である場合は JSON 文字列には何も追加されません。
  • undefined を返した場合、そのプロパティは出力する JSON 文字列には含まれません。

メモ: replacer 関数を使用して配列から値を削除することはできません。 undefined や関数を返すと、代わりに null が使用されます。

メモ: replacer が、オブジェクトが初期状態であるか、空文字列のキーを持つプロパティであるかを判別するには (どちらの場合もキーが空文字列になり、オブジェクトが値になる可能性があるので)、繰り返し回数を追跡しておく必要があります (繰り返しが1回を超えた場合、空文字列のキーであることが分かります)。

関数を使った例

function replacer(key, value) {
  // Filtering out properties
  if (typeof value === 'string') {
    return undefined;
  }
  return value;
}

var foo = {foundation: 'Mozilla', model: 'box', week: 45, transport: 'car', month: 7};
JSON.stringify(foo, replacer);
// '{"week":45,"month":7}'

配列を使った例

replacer が配列である場合、その配列の値は結果の JSON 文字列に含めるプロパティの名前を示します。

JSON.stringify(foo, ['week', 'month']);  
// '{"week":45,"month":7}', "week" と "month" プロパティだけが保持される

space 引数

space 引数で最終的な文字列でのスペーシングを調整できます。

  • 数値であれば、レベルの階層がそれぞれその数の空白文字(最大10文字)でインデントされます。
  • 文字列であれば、レベル階層がそれぞれこの文字列(またはその最初の10文字)でインデントされます。
JSON.stringify({ a: 2 }, null, ' ');
// '{
//  "a": 2
// }'

タブ文字を使うと、標準的な表示の整形と同様になります。

JSON.stringify({ uno: 1, dos: 2 }, null, '\t');
// returns the string:
// '{
//     "uno": 1,
//     "dos": 2
// }'

toJSON() の挙動

文字列化されるオブジェクトに toJSON という名前の値に関数を持ったプロパティがある場合、その toJSON() メソッドで JSON の文字列化の挙動をカスタマイズできます。シリアライズされるオブジェクトの代わりに、その toJSON() メソッドが呼び出されたときの返値がシリアライズされます。 JSON.stringify()toJSON に以下のどれか1つの引数をつけて呼び出します。

  • このオブジェクトがプロパティの値であった場合は、プロパティ名
  • 配列内の値で会った場合は、配列のインデックス番号を文字列で
  • JSON.stringify() がこのオブジェクトに対して直接呼び出された場合は、空文字列

var obj = {
    data: 'data',
    
    toJSON (key) {
        if (key)
            return `Now I am a nested object under key '${key}'`;
        else
            return this;
    }
};

JSON.stringify(obj);
// '{"data":"data"}'

JSON.stringify({ obj })
// '{"obj":"Now I am a nested object under key 'obj'"}'

JSON.stringify([ obj ])
// '["Now I am a nested object under key '0'"]'

循環参照をシリアライズする時の JSON.stringify() に関する問題 

JSON 形式はオブジェクト参照に対応していないため (IETF 草稿はありますが)、循環参照のあるオブジェクトをエンコードしようとすると TypeError が発生します。

const circularReference = {};
circularReference.myself = circularReference;

// 循環参照をシリアライズすると "TypeError: cyclic object value" が発生
JSON.stringify(circularReference);

循環参照をシリアライズするためには、これに対応したライブラリを使用したり (Douglas Crockford による cycle.js など)、自分自身で解決策を実装したりする方法があります。循環参照を探索してシリアライズされた値に置き換える (または削除する) 必要があるでしょう。

JavaScript としての使用に際してのそのままの JSON.stringify の問題

従来、 JSON は JavaScript の完全に厳密なサブセットではありませんでした。文字コードポイント U+2028 LINE SEPARATOR (改行) と U+2029 PARAGRAPH SEPARATOR (改段落) は JSON テキスト内の文字列リテラルやプロパティ名に使用することができます。しかし、 JavsScript のテキスト内で同様の文脈では使用することができません -- Unicode エスケープを使用した \u2028 および \u2029 しか使うことができません。これは最近変更され、どちらのコードポイントも JSON と JavaScript の両方の文字列で使用することができるようになりました。

したがって、古い JavaScript エンジンとの互換性が必要な場合は、 JSON.stringify から返された文字列を JavaScript の文字列に代入するために、直接 evalnew Function に渡したり、 JSONP URL の一部として用いたりするのは危険です。次のユーティリティを使用することができます。

function jsFriendlyJSONStringify (s) {
    return JSON.stringify(s).
        replace(/\u2028/g, '\\u2028').
        replace(/\u2029/g, '\\u2029');
}

var s = {
    a: String.fromCharCode(0x2028),
    b: String.fromCharCode(0x2029)
};
try {
    eval('(' + JSON.stringify(s) + ')');
} catch (e) {
    console.log(e); // "SyntaxError: unterminated string literal"
}

// catch する必要はない
eval('(' + jsFriendlyJSONStringify(s) + ')');

// Firefox での console.log はコンソールにログ出力をする場合
//   Unicode エスケープを解除するので、alert を使う
alert(jsFriendlyJSONStringify(s)); // {"a":"\u2028","b":"\u2029"}

メモ: 配列以外のオブジェクトのプロパティでは、特定の順番で文字列化されることは保証されていません。文字列化された同じオブジェクトの中でプロパティの順番に依存しないようにしてください。

var a = JSON.stringify({ foo: "bar", baz: "quux" })
//'{"foo":"bar","baz":"quux"}'
var b = JSON.stringify({ baz: "quux", foo: "bar" })
//'{"baz":"quux","foo":"bar"}'
console.log(a !== b) // true

// 一部の記憶関数は JSON.stringify を使用して引数をシリアライズしており、
// 上記のような同じオブジェクトに出会った時にキャッチし損ねることがあります

localStorageJSON.stringify() を使った例

ユーザーが作成したオブジェクトを格納し、ブラウザーが閉じた後に復元できるようにしたい場合は以下の例が JSON.stringify() を適用した模範例です。

// JSON の一例を作成
var session = {
  'screens': [],
  'state': true
};
session.screens.push({ 'name': 'screenA', 'width': 450, 'height': 250 });
session.screens.push({ 'name': 'screenB', 'width': 650, 'height': 350 });
session.screens.push({ 'name': 'screenC', 'width': 750, 'height': 120 });
session.screens.push({ 'name': 'screenD', 'width': 250, 'height': 60 });
session.screens.push({ 'name': 'screenE', 'width': 390, 'height': 120 });
session.screens.push({ 'name': 'screenF', 'width': 1240, 'height': 650 });

// JSON.stringify() で JSON 文字列に変換してから
// session の名前で localStorage に保存
localStorage.setItem('session', JSON.stringify(session));

// JSON.stringify() で生成されて localStorage に保存された文字列を
// 再び JSON オブジェクトに変換する方法の例
var restoredSession = JSON.parse(localStorage.getItem('session'));

// ここで変数 restoredSession には localStorage に保存されていた
// オブジェクトが入っている
console.log(restoredSession);

Well-formed JSON.stringify()

well-formed JSON.stringify 仕様を実装しているエンジンは、サロゲート文字 -- U+D800 から U+DFFF までのすべてのコードポイント -- を、リテラルではなく Unicode エスケープシーケンスを使用して文字列化します。この変更前は、入力にサロゲート文字が含まれている場合、 JSON.stringify はサロゲート文字をそのまま出力していました。このような文字列は妥当な UTF-8 または UTF-16 でエンコードされていませんでした。

JSON.stringify("\uD800"); // '"�"'

しかし、この変更で JSON.stringify は lone surrogates を JSON エスケープシーケンスによって表すようになり、妥当な UTF-8 または UTF-16 でエンコードすることができるようになりました。

JSON.stringify("\uD800"); // '"\\ud800"'

この変更では、サロゲート文字の Unicode エスケープをサロゲート文字と同一のものとして扱うため、 JSON.stringify の結果を、 JSON テキストを妥当である限りどのようなものでも受け付ける JSON.parse のような API に渡したときに後方互換性があります。 JSON.stringify の結果を直接解析する場合のみJSON.stringify がこれらのコードポイントに対して2通りのエンコーディングをする可能性があることに注意して扱う必要があります。

仕様書

仕様書 状態 備考
ECMAScript Latest Draft (ECMA-262)
JSON.stringify の定義
ドラフト
ECMAScript 2015 (6th Edition, ECMA-262)
JSON.stringify の定義
標準
ECMAScript 5.1 (ECMA-262)
JSON.stringify の定義
標準 初回定義。JavaScript 1.7 で実装。

ブラウザーの互換性

Update compatibility data on GitHub
デスクトップモバイルサーバー
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeAndroid 版 FirefoxAndroid 版 OperaiOSのSafariSamsung InternetNode.js
stringifyChrome 完全対応 3Edge 完全対応 12Firefox 完全対応 3.5IE 完全対応 8Opera 完全対応 10.5Safari 完全対応 4WebView Android 完全対応 ≤37Chrome Android 完全対応 18Firefox Android 完全対応 4Opera Android 完全対応 11Safari iOS 完全対応 ありSamsung Internet Android 完全対応 1.0nodejs 完全対応 あり
Well-formed JSON.stringifyChrome 完全対応 72Edge 未対応 なしFirefox 完全対応 64IE 未対応 なしOpera 完全対応 60Safari 完全対応 12.1WebView Android 完全対応 72Chrome Android 完全対応 72Firefox Android 完全対応 64Opera Android 完全対応 50Safari iOS 完全対応 12.2Samsung Internet Android 未対応 なしnodejs 未対応 なし

凡例

完全対応  
完全対応
未対応  
未対応

関連情報

  • JSON.parse()
  • cycle.js – Introduces two functions: JSON.decycle and JSON.retrocycle. These allow encoding and decoding of cyclical structures and DAGs into an extended and retrocompatible JSON format.