1. ホーム
  2. javascript

[解決済み] 周期的なオブジェクト値を含むオブジェクトのシリアライズ

2022-02-11 14:59:42

質問

他のノードへの参照である子ノードを含むオブジェクト(構文解析ツリー)があります。

このオブジェクトをシリアル化するために JSON.stringify() しかし、私は

TypeError: オブジェクトの値が循環しています。

というのは、私が述べた構成があるからです。

どうすれば回避できるのでしょうか?他のノードへのこれらの参照がシリアライズされたオブジェクトで表現されているかどうかは私には重要ではありません。

一方、オブジェクトを作成するときにこれらのプロパティを削除するのは面倒そうだし、パーサー(narcissus)に変更を加えるのも嫌だ。

解決方法は?

の第2パラメータを使用します。 stringify は、その 置き換え機能 既にシリアライズされたオブジェクトを除外するため。

var seen = [];

JSON.stringify(obj, function(key, val) {
   if (val != null && typeof val == "object") {
        if (seen.indexOf(val) >= 0) {
            return;
        }
        seen.push(val);
    }
    return val;
});

http://jsfiddle.net/mH6cJ/38/

他のコメントで正しく指摘されているように、このコードは "recursive" だけでなく、すべての "seen" のオブジェクトを削除します。

例えば、以下のような場合。

a = {x:1};
obj = [a, a];

の場合、結果は不正確なものになります。このような構造の場合、Crockfordの デシクル または、再帰参照をヌルに置き換えるだけのこの(より単純な)関数があります。

function decycle(obj, stack = []) {
    if (!obj || typeof obj !== 'object')
        return obj;
    
    if (stack.includes(obj))
        return null;

    let s = stack.concat([obj]);

    return Array.isArray(obj)
        ? obj.map(x => decycle(x, s))
        : Object.fromEntries(
            Object.entries(obj)
                .map(([k, v]) => [k, decycle(v, s)]));
}

//

let a = {b: [1, 2, 3]}
a.b.push(a);

console.log(JSON.stringify(decycle(a)))