我有一个大对象要转换成JSON并发送。然而,它具有圆形结构。我想丢弃任何存在的循环引用,并发送任何可以字符串化的引用。我该怎么做?
谢谢
var obj = {
a: "foo",
b: obj
}
我想将对象字符串化为:
{"a":"foo"}
我有一个大对象要转换成JSON并发送。然而,它具有圆形结构。我想丢弃任何存在的循环引用,并发送任何可以字符串化的引用。我该怎么做?
谢谢
var obj = {
a: "foo",
b: obj
}
我想将对象字符串化为:
{"a":"foo"}
当前回答
循环引用时,此代码将失败:
JSON.stringify(circularReference);
// TypeError: cyclic object value
使用以下代码:
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());
其他回答
我想知道为什么还没有人从MDN页面发布正确的解决方案。。。
const circular Reference={otherData:123};circular Reference.imy=circular参考;常量getCircularReplacer=()=>{const seed=new WeakSet();return(键,值)=>{if(typeof value==“object”&&value!==null){if(seed.has(value)){回来}见add(值);}返回值;};};const stringified=JSON.stringify(circularReference,getCircularReplacer());console.log(字符串化);
Seen值应该存储在集合中,而不是数组中(每个元素都会调用replacer)。
与公认的答案一样,此解决方案删除了所有重复值,而不仅仅是循环值。但至少它没有指数级的复杂性。
我真的很喜欢Trindaz的解决方案——更详细,但它有一些缺陷。我也为喜欢它的人修过。
此外,我对缓存对象添加了长度限制。
如果我打印的对象真的很大-我的意思是无限大-我想限制我的算法。
JSON.stringifyOnce = function(obj, replacer, indent){
var printedObjects = [];
var printedObjectKeys = [];
function printOnceReplacer(key, value){
if ( printedObjects.length > 2000){ // browsers will not print more than 20K, I don't see the point to allow 2K.. algorithm will not be fast anyway if we have too many objects
return 'object too long';
}
var printedObjIndex = false;
printedObjects.forEach(function(obj, index){
if(obj===value){
printedObjIndex = index;
}
});
if ( key == ''){ //root element
printedObjects.push(obj);
printedObjectKeys.push("root");
return value;
}
else if(printedObjIndex+"" != "false" && typeof(value)=="object"){
if ( printedObjectKeys[printedObjIndex] == "root"){
return "(pointer to root)";
}else{
return "(see " + ((!!value && !!value.constructor) ? value.constructor.name.toLowerCase() : typeof(value)) + " 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;
}
我这样解决这个问题:
var util = require('util');
// Our circular object
var obj = {foo: {bar: null}, a:{a:{a:{a:{a:{a:{a:{hi: 'Yo!'}}}}}}}};
obj.foo.bar = obj;
// Generate almost valid JS object definition code (typeof string)
var str = util.inspect(b, {depth: null});
// Fix code to the valid state (in this example it is not required, but my object was huge and complex, and I needed this for my case)
str = str
.replace(/<Buffer[ \w\.]+>/ig, '"buffer"')
.replace(/\[Function]/ig, 'function(){}')
.replace(/\[Circular]/ig, '"Circular"')
.replace(/\{ \[Function: ([\w]+)]/ig, '{ $1: function $1 () {},')
.replace(/\[Function: ([\w]+)]/ig, 'function $1(){}')
.replace(/(\w+): ([\w :]+GMT\+[\w \(\)]+),/ig, '$1: new Date("$2"),')
.replace(/(\S+): ,/ig, '$1: null,');
// Create function to eval stringifyed code
var foo = new Function('return ' + str + ';');
// And have fun
console.log(JSON.stringify(foo(), null, 4));
这里有一个解决方案:
仅删除循环(而不是所有重复的对象引用,到目前为止这里发布的大多数解决方案也是如此),不是不必要的冗长,速度快,不需要任何库依赖关系。
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]'}}"