我有一个大对象,想要转换为JSON并发送。但是它具有圆形结构。我想扔掉任何存在的循环引用并发送任何可以字符串化的东西。我怎么做?
谢谢。
var obj = {
a: "foo",
b: obj
}
我想将obj字符串化为:
{"a":"foo"}
我有一个大对象,想要转换为JSON并发送。但是它具有圆形结构。我想扔掉任何存在的循环引用并发送任何可以字符串化的东西。我怎么做?
谢谢。
var obj = {
a: "foo",
b: obj
}
我想将obj字符串化为:
{"a":"foo"}
Try this:
var obj = {
a: "foo",
b: obj
};
var circular_replacer = (value) => {
var seen = [];
if (value != null && typeof value == "object") {
if (seen.indexOf(value) >= 0) return;
seen.push(value);
}
return value;
};
obj = circular_replacer(obj);
我在github上发现了circular-json库,它可以很好地解决我的问题。
我发现一些有用的好功能:
我知道这是一个古老的问题,但是我想建议我创建一个名为smart-circular的NPM软件包,该软件包的工作方式与其他建议的方式不同。如果您使用大而深的物体,则特别有用。
一些功能是:
用导致对象第一次出现的路径替换对象内的圆形引用或简单重复的结构(不仅仅是字符串[circular]);
通过在广度优先搜索中查找圆度,程序包可确保此路径尽可能小,这在处理非常大和很深的对象时非常重要,在这种情况下,这些路径会变得很长且难以遵循( JSON.stringify做一个DFS);
允许个性化替换,方便简化或忽略对象中次要的部分;
最后,路径将按照访问引用字段所必需的方式精确编写,这可以帮助您进行调试。
对于将来的Google员工,当您不知道所有循环引用的键时都在寻找解决方案,可以使用JSON.stringify函数周围的包装器来排除循环引用。请参阅https://gist.github.com/4653128上的示例脚本。
解决方案本质上可以归结为保留对数组中先前打印的对象的引用,并在返回值之前在替换函数中进行检查。它比仅排除循环引用更严格,因为它还排除了两次打印对象的副作用,其中之一是避免使用循环引用。
包装器示例:
function stringifyOnce(obj, replacer, indent){
var printedObjects = [];
var printedObjectKeys = [];
function printOnceReplacer(key, value){
var printedObjIndex = false;
printedObjects.forEach(function(obj, index){
if(obj===value){
printedObjIndex = index;
}
});
if(printedObjIndex && typeof(value)=="object"){
return "(see " + value.constructor.name.toLowerCase() + " with key " + printedObjectKeys[printedObjIndex] + ")";
}else{
var qualifiedKey = key || "(empty key)";
printedObjects.push(value);
printedObjectKeys.push(qualifiedKey);
if(replacer){
return replacer(key, value);
}else{
return value;
}
}
}
return JSON.stringify(obj, printOnceReplacer, indent);
}
var a={b:"b"};
a.a=a;
JSON.stringify(preventCircularJson(a));
计算结果为:
"{"b":"b","a":"CIRCULAR_REFERENCE_REMOVED"}"
具有以下功能:
/**
* Traverses a javascript object, and deletes all circular values
* @param source object to remove circular references from
* @param censoredMessage optional: what to put instead of censored values
* @param censorTheseItems should be kept null, used in recursion
* @returns {undefined}
*/
function preventCircularJson(source, censoredMessage, censorTheseItems) {
//init recursive value if this is the first call
censorTheseItems = censorTheseItems || [source];
//default if none is specified
censoredMessage = censoredMessage || "CIRCULAR_REFERENCE_REMOVED";
//values that have allready apeared will be placed here:
var recursiveItems = {};
//initaite a censored clone to return back
var ret = {};
//traverse the object:
for (var key in source) {
var value = source[key]
if (typeof value == "object") {
//re-examine all complex children again later:
recursiveItems[key] = value;
} else {
//simple values copied as is
ret[key] = value;
}
}
//create list of values to censor:
var censorChildItems = [];
for (var key in recursiveItems) {
var value = source[key];
//all complex child objects should not apear again in children:
censorChildItems.push(value);
}
//censor all circular values
for (var key in recursiveItems) {
var value = source[key];
var censored = false;
censorTheseItems.forEach(function (item) {
if (item === value) {
censored = true;
}
});
if (censored) {
//change circular values to this
value = censoredMessage;
} else {
//recursion:
value = preventCircularJson(value, censoredMessage, censorChildItems.concat(censorTheseItems));
}
ret[key] = value
}
return ret;
}
const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
};
JSON.stringify(circularReference, getCircularReplacer());
看到的值应存储在set中,而不是数组中(在每个element上调用replacer ),并且无需尝试链中导致循环引用的JSON.stringify
每个元素。
就像在接受的答案中一样,此解决方案会删除所有重复值,而不仅仅是循环值。但是至少它没有指数复杂性。
Although this has been answered sufficiently, you could also explicitly delete the property in question before stringification using the
delete
operator.delete operator
this will remove the need to build or maintain complex logic to remove circular references.