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

2020/11/10 02:42 · javascript ·  · 0评论

我有一个对象(解析树),其中包含子节点,这些子节点是对其他节点的引用。

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

TypeError:循环对象值

因为我提到的结构。

我该如何解决?对我而言,在序列化对象中是否表示对其他节点的引用并不重要。

另一方面,在创建对象时从对象中删除这些属性似乎很繁琐,并且我不想更改解析器(水仙)。

使用的第二个参数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/

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

例如,用于:

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

结果将不正确。如果您的结构是这样的,则可能要使用Crockford的decycle或此(简单的)函数,该函数仅将递归引用替换为null:

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)))

这是一种替代答案,但是由于很多人来这里调试它们的圆形对象,而且如果不引入大量代码,实际上没有什么好办法可以做到这一点。

不那么知名的一项功能JSON.stringify()console.table()只需调用console.table(whatever);,它将以表格格式将变量记录在控制台中,从而使读取变量的内容变得相当容易和方便。

这是带有循环引用的数据结构的示例:
工具仓库

function makeToolshed(){
    var nut = {name: 'nut'}, bolt = {name: 'bolt'};
    nut.needs = bolt; bolt.needs = nut;
    return { nut: nut, bolt: bolt };
}

当你想KEEP循环引用(当你反序列化恢复它们,而不是“的摧毁”),你有2个选择,我会在这里进行比较。首先是Douglas Crockford的cycle.js,其次是我的西伯利亚软件包。两者都通过首先“回收”对象来工作,即构造“包含相同信息”的另一个对象(没有任何循环引用)。

克罗克福德先生先行:

JSON.decycle(makeToolshed())

JSON_decycleMakeToolshed

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

root = makeToolshed();
[root.bolt === root.nut.needs, root.nut.needs.needs === root.nut]; // retutrns [true,true]

美元符号代表根。.bolt$ref告诉我们,.bolt是“已经看到”对象,特殊属性的值(在这里,字符串$ [“螺母”] [“需求”])告诉我们哪里,最先看到的===上面。同样适用于第二$ref和第二===

让我们使用一个合适的深度平等测试(即deepGraphEqual从对这个问题的公认答案中的Anders Kaseorg函数)来看克隆是否有效。

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

现在,西伯利亚:

JSON.Siberia.forestify(makeToolshed())

JSON_Siberia_forestify_makeToolshed

西伯利亚不会尝试模仿“经典” JSON,没有嵌套结构。对象图以“平面”方式描述。对象图的每个节点都变成一棵扁平树(带有纯整数值的纯键值对列表),它是.forest.索引0处的根对象,找到索引的根对象,索引高处则查找的其他节点。对象图和负值(林中某棵树的某些键的负值)指向atoms数组(该数组是通过类型数组键入的,但此处将跳过键入细节)。所有终端节点都在原子表中,所有非终端节点都在林表中,您可以立即看到对象图有多少个节点,即forest.length让我们测试一下是否可行:

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月)发生

嗯,和超短版比较。对于“指针”,我需要尽可能多的空间整数需要,因为我的“指针已经看到节点”(作为事实上,所有节点,已经看到与否)只是整数。在克罗克福德先生的版本中,存储“指针”所需的数量仅受对象图的大小限制。这使克罗克福德先生的版本最糟糕的情况变得极其可怕克罗克福德先生给了我们“另一个Bubblesort”。我不是在开玩笑。真不好 如果您不相信它,可以进行测试,您可以从软件包的自述文件中找到它们(也将在本月2020年8月将它们转换为与Benchmark.js兼容)。

我创建了一个GitHub Gist,它能够检测循环结构并对其进行解编码并进行编码:https : //gist.github.com/Hoff97/9842228

要进行转换,只需使用JSONE.stringify / JSONE.parse。它还对功能进行解编码。如果要禁用此功能,只需删除第32-48和61-85行即可。

var strg = JSONE.stringify(cyclicObject);
var cycObject = JSONE.parse(strg);

您可以在此处找到一个小提琴示例:

http://jsfiddle.net/hoff97/7UYd4/

更省力,它显示了循环对象的位置。

<script>
var jsonify=function(o){
    var seen=[];
    var jso=JSON.stringify(o, function(k,v){
        if (typeof v =='object') {
            if ( !seen.indexOf(v) ) { return '__cycle__'; }
            seen.push(v);
        } return v;
    });
    return jso;
};
var obj={
    g:{
        d:[2,5],
        j:2
    },
    e:10
};
obj.someloopshere = [
    obj.g,
    obj,
    { a: [ obj.e, obj ] }
];
console.log('jsonify=',jsonify(obj));
</script>

产生

jsonify = {"g":{"d":[2,5],"j":2},"e":10,"someloopshere":[{"d":[2,5],"j":2},"__cycle__",{"a":[10,"__cycle__"]}]}

我也创建了一个github项目,该项目可以序列化循环对象并还原类(如果将其保存在诸如String的serializename属性中)

var d={}
var a = {b:25,c:6,enfant:d};
d.papa=a;
var b = serializeObjet(a);
assert.equal(  b, "{0:{b:25,c:6,enfant:'tab[1]'},1:{papa:'tab[0]'}}" );
var retCaseDep = parseChaine(b)
assert.equal(  retCaseDep.b, 25 );
assert.equal(  retCaseDep.enfant.papa, retCaseDep );

https://github.com/bormat/serializeStringifyParseCyclicObject

编辑:我已经将我的脚本转换为NPM https://github.com/bormat/borto_circular_serialize,并且我将函数名称从法语更改为英语。

function stringifyObject ( obj ) {
  if ( _.isArray( obj ) || !_.isObject( obj ) ) {
    return obj.toString()
  }
  var seen = [];
  return JSON.stringify(
    obj,
    function( key, val ) {
      if (val != null && typeof val == "object") {
        if ( seen.indexOf( val ) >= 0 )
          return
          seen.push( val )
          }
      return val
    }
  );
}

缺少前提条件,否则数组对象中的整数值将被截断,即[[08.11.2014 12:30:13,1095]] 1095减少为095。

本文地址:http://javascript.askforanswer.com/xuliehuabaohanxunhuanduixiangzhideduixiang.html
文章标签: ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!