我有一个对象(解析树),其中包含子节点,这些子节点是对其他节点的引用。
我想使用序列化此对象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;
});
正如在其他注释中正确指出的那样,此代码删除了每个“可见”对象,而不仅仅是“递归”对象。
例如,用于:
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的嵌套结构得以保留,但是有一个新东西,即具有特殊$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,没有嵌套结构。对象图以“平面”方式描述。对象图的每个节点都变成一棵扁平树(带有纯整数值的纯键值对列表),它是.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。
文章标签:javascript , json , jsonserializer , stringify
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!
评论已关闭!