我有一个大对象要转换成JSON并发送。然而,它具有圆形结构。我想丢弃任何存在的循环引用,并发送任何可以字符串化的引用。我该怎么做?
谢谢
var obj = {
a: "foo",
b: obj
}
我想将对象字符串化为:
{"a":"foo"}
我有一个大对象要转换成JSON并发送。然而,它具有圆形结构。我想丢弃任何存在的循环引用,并发送任何可以字符串化的引用。我该怎么做?
谢谢
var obj = {
a: "foo",
b: obj
}
我想将对象字符串化为:
{"a":"foo"}
当前回答
JSON.stringify()的第二个参数还允许您指定一个关键字名称数组,该数组应保存在数据中遇到的每个对象中。这可能不适用于所有用例,但却是一个简单得多的解决方案。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
var obj = {
a: "foo",
b: this
}
var json = JSON.stringify(obj, ['a']);
console.log(json);
// {"a":"foo"}
注意:奇怪的是,OP中的对象定义在最新的Chrome或Firefox中没有抛出循环引用错误。这个答案中的定义被修改了,所以它确实抛出了一个错误。
其他回答
使用带有自定义替换符的JSON.stringify。例如:
// Demo: Circular reference
var circ = {};
circ.circ = circ;
// Note: cache should not be re-used by repeated calls to JSON.stringify.
var cache = [];
JSON.stringify(circ, (key, value) => {
if (typeof value === 'object' && value !== null) {
// Duplicate reference found, discard key
if (cache.includes(value)) return;
// Store value in our collection
cache.push(value);
}
return value;
});
cache = null; // Enable garbage collection
本例中的替换符并非100%正确(取决于您对“重复”的定义)。在以下情况下,将丢弃一个值:
var a = {b:1}
var o = {};
o.one = a;
o.two = a;
// one and two point to the same object, but two is discarded:
JSON.stringify(o, ...);
但概念是:使用自定义替换器,并跟踪解析的对象值。
作为es6中编写的实用函数:
// safely handles circular references
JSON.safeStringify = (obj, indent = 2) => {
let cache = [];
const retVal = JSON.stringify(
obj,
(key, value) =>
typeof value === "object" && value !== null
? cache.includes(value)
? undefined // Duplicate reference found, discard key
: cache.push(value) && value // Store value in our collection
: value,
indent
);
cache = null;
return retVal;
};
// Example:
console.log('options', JSON.safeStringify(options))
我们使用对象扫描进行数据处理,这可能是一个可行的解决方案。这就是它的工作方式(也可以正确修剪数组)
.作为控制台包装{最大高度:100%!重要;顶部:0}<script type=“module”>导入对象扫描自'https://cdn.jsdelivr.net/npm/object-scan@18.1.2/lib/index.min.js';const prune=(data)=>对象扫描(['**']{rtn:'计数',filterFn:({isCircular,父级,属性})=>{if(isCircular){if(Array.isArray(父级)){parent.splice(属性,1);}其他{删除父项[属性];}返回true;}return false;},breakFn:({isCircular})=>isCircular==true})(数据);常量obj={a:'foo',c:[0]};obj.b=对象;obj.c.push(obj);console.log(obj);//=><ref*1>{a:'foo',c:[0,[Circular*1]],b:[Circulal*1]}console.log(prune(obj));//返回循环计数// => 2console.log(obj);//=>{a:'foo',c:[0]}</script>
免责声明:我是物体扫描的作者
注意,还有一个由Douglas Crockford实现的JSON.decycle方法。看看他的cycle.js。这允许您对几乎任何标准结构进行字符串化:
var a = [];
a[0] = a;
a[1] = 123;
console.log(JSON.stringify(JSON.decycle(a)));
// result: '[{"$ref":"$"},123]'.
您也可以使用逆循环方法重新创建原始对象。因此,您不必从对象中删除循环来将其字符串化。
然而,这对于DOM节点(在现实生活中,这是周期的典型原因)不起作用。例如,这将抛出:
var a = [document.body];
console.log(JSON.stringify(JSON.decycle(a)));
我做了一个叉来解决这个问题(参见我的cycle.js叉)。这应该很好:
var a = [document.body];
console.log(JSON.stringify(JSON.decycle(a, true)));
注意,在我的fork中,JSON.decycle(变量)的工作方式与原始一样,当变量包含DOM节点/元素时,将抛出异常。
当您使用JSON.decycle(variable,true)时,您接受这样一个事实,即结果是不可逆的(retrocycle不会重新创建DOM节点)。但DOM元素在某种程度上应该是可识别的。例如,如果一个div元素有一个id,那么它将被替换为字符串“div#id of the element”。
本线程中的大多数答案都是专门针对JSON.stringify使用的——它们没有显示如何实际删除原始对象树中的循环引用。(好吧,除了之后再次调用JSON.parse之外——这需要重新分配,并且会产生更高的性能影响)
要从源对象树中删除循环引用,可以使用以下函数:https://stackoverflow.com/a/63952549/2441655
然后,可以使用这些通用的循环引用移除器函数安全地调用循环引用敏感函数(如JSON.stringify):
const objTree = {normalProp: true};
objTree.selfReference = objTree;
RemoveCircularLinks(objTree); // without this line, the JSON.stringify call errors
console.log(JSON.stringify(objTree));
这里有一个解决方案:
仅删除循环(而不是所有重复的对象引用,到目前为止这里发布的大多数解决方案也是如此),不是不必要的冗长,速度快,不需要任何库依赖关系。
function replaceCycles(obj, replacement = undefined, seen = new WeakSet()) {
if (typeof obj === 'object')
if (seen.has(obj))
return replacement
else {
seen.add(obj)
const newObj = {}
for (const key in obj)
newObj[key] = replaceCycles(obj[key], replacement, seen)
seen.delete(obj)
return newObj
}
else
return obj
}
用法:
const a = {
b: 'v1',
c: {
d: 'v2'
}
}
a.e = a.c
a.c.f = a.c
console.log(JSON.stringify(replaceCycles(a, '[CYCLE]')))
输出:
"{'b':'v1','c':{'d':'v2','f':'[CYCLE]'},'e':{'d':'v2','f':'[CYCLE]'}}"