首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >序列化包含循环对象值的对象

序列化包含循环对象值的对象
EN

Stack Overflow用户
提问于 2012-02-22 01:27:29
回答 8查看 150.4K关注 0票数 168

我有一个对象(parse tree),它包含引用其他节点的子节点。

我想使用JSON.stringify()来序列化这个对象,但是我得到了

TypeError:循环对象值

因为我提到的构造。

我该如何解决这个问题呢?这些对其他节点的引用是否在序列化对象中表示对我来说并不重要。

另一方面,在创建这些属性时将它们从对象中删除似乎很乏味,而且我不想对解析器(narcissus)进行更改。

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2012-02-22 01:41:17

使用stringify的第二个参数replacer function来排除已经序列化的对象:

代码语言:javascript
复制
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/

正如在其他注释中正确指出的那样,此代码删除了每个“可见”对象,而不仅仅是“递归”对象。

例如,对于:

代码语言:javascript
复制
a = {x:1};
obj = [a, a];

结果将是不正确的。如果你的结构是这样的,你可能想要使用克罗克福德的decycle或者这个(更简单的)函数,它只是将递归引用替换为null:

代码语言:javascript
复制
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)))

票数 244
EN

Stack Overflow用户

发布于 2020-07-11 01:26:02

这是一种替代答案,但由于许多人来这里的目的是调试他们的循环对象,而没有一种很好的方法可以在不引入大量代码的情况下做到这一点,所以这里就是这样。

console.table()是一个没有JSON.stringify()那么出名的特性。只需调用console.table(whatever);,它将以表格格式将变量记录在控制台中,这使得仔细阅读变量的内容变得相当容易和方便。

票数 8
EN

Stack Overflow用户

发布于 2019-11-12 10:04:29

下面是一个具有循环引用的数据结构示例:

代码语言:javascript
复制
function makeToolshed(){
    var nut = {name: 'nut'}, bolt = {name: 'bolt'};
    nut.needs = bolt; bolt.needs = nut;
    return { nut: nut, bolt: bolt };
}

当你想保持循环引用()(在反序列化时恢复它们,而不是“‘ll”它们),你有两个选择,我将在这里进行比较。首先是Douglas Crockford的cycle.js,其次是我的siberia包。两者的工作方式都是首先“解循环”对象,即构造“包含相同信息”的另一个对象(没有任何循环引用)。

克罗克福德先生先说:

代码语言:javascript
复制
JSON.decycle(makeToolshed())

如您所见,JSON的嵌套结构保持不变,但有了一个新东西,即具有特殊$ref属性的对象。让我们看看它是如何工作的。

代码语言:javascript
复制
root = makeToolshed();
[root.bolt === root.nut.needs, root.nut.needs.needs === root.nut]; // retutrns [true,true]

美元符号代表词根。具有$ref的坚果告诉我们.bolt是一个“已经看到”的对象,而这个特殊属性的值(这里是字符串$“.bolt”)告诉我们在哪里,请参见上面的第一个===。上面的第二个$ref和第二个===也是如此。

让我们使用合适的深度相等性测试(即Anders Kaseorg对this question的公认答案中的deepGraphEqual函数)来查看克隆是否有效。

代码语言:javascript
复制
root = makeToolshed();
clone = JSON.retrocycle(JSON.decycle(root));
deepGraphEqual(root, clone) // true
serialized = JSON.stringify(JSON.decycle(root));
clone2 = JSON.retrocycle(JSON.parse(serialized));
deepGraphEqual(root, clone2); // true

现在,西伯利亚:

代码语言:javascript
复制
JSON.Siberia.forestify(makeToolshed())

西伯利亚没有试图模仿“经典”的JSON,没有嵌套结构。对象图是以“扁平”的方式描述的。对象图的每个节点都被转换成一个扁平树(纯键值对列表,它是.forest.中索引为0的条目),我们找到根对象,在更高的索引中,我们找到对象图的其他节点,负值(森林的某些树的某个键的负值)指向atoms数组(它是通过类型数组键入的,但我们在这里跳过键入的细节)。所有终端节点都在atoms表中,所有非终端节点都在森林表中,您可以立即看到对象图有多少个节点,即forest.length。让我们测试一下它是否正常工作:

代码语言:javascript
复制
root = makeToolshed();
clone = JSON.Siberia.unforestify(JSON.Siberia.forestify(root));
deepGraphEqual(root, clone); // true
serialized = JSON.Siberia.stringify(JSON.Siberia.forestify(root));
clone2 = JSON.Siberia.unforestify(JSON.Siberia.unstringify(serialized));
deepGraphEqual(root, clone2); // true

比较

稍后将添加部分。

备注

我现在正在重构这个包。核心思想和算法保持不变,但新版本将更容易使用,顶级API将有所不同。我很快就会对西伯利亚进行归档,并展示重构后的版本,我称之为objectgraph。敬请期待,这将在本月(2020年8月)发生。

啊,还有超短版比较一下。对于“指针”,我需要一个整数占用的空间,因为我的“指向已经看到的节点的指针”(事实上,指向所有节点的指针,不管是否已经看到)都是整数。在Crockford先生的版本中,存储“指针”所需的量仅受对象图大小的限制。这使得克罗克福德的版本在最坏的情况下变得极其复杂。克罗克福德先生给了我们“另一个泡泡”。我没跟你开玩笑。真的很糟糕。如果你不相信,有一些测试,你可以从软件包的自述文件中找到它们(将在这个月,2020年8月将它们转换为benchmark.js兼容)

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9382167

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档