我有一个大对象要转换成JSON并发送。然而,它具有圆形结构。我想丢弃任何存在的循环引用,并发送任何可以字符串化的引用。我该怎么做?

谢谢

var obj = {
  a: "foo",
  b: obj
}

我想将对象字符串化为:

{"a":"foo"}

当前回答

使用此类对象解决此问题的另一种解决方案是使用此库

https://github.com/ericmuyser/stringy

它很简单,你可以在几个简单的步骤中解决这个问题。

其他回答

@RobW的答案是正确的,但这更有表现力!因为它使用了hashmap/set:

const customStringify = function (v) {
  const cache = new Set();
  return JSON.stringify(v, function (key, value) {
    if (typeof value === 'object' && value !== null) {
      if (cache.has(value)) {
        // Circular reference found
        try {
          // If this value does not reference a parent it can be deduped
         return JSON.parse(JSON.stringify(value));
        }
        catch (err) {
          // discard key if value cannot be deduped
         return;
        }
      }
      // Store value in our set
      cache.add(value);
    }
    return value;
  });
};

我们使用对象扫描进行数据处理,这可能是一个可行的解决方案。这就是它的工作方式(也可以正确修剪数组)

.作为控制台包装{最大高度: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>

免责声明:我是物体扫描的作者

我真的很喜欢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);
};

我知道这个问题很老,有很多很好的答案,但我发布这个答案是因为它有新的味道(es5+)

Object.defineProperties(JSON{refStringify:{值:函数(obj){let objMap=new Map();let stringified=JSON.stringify(obj,函数(键、值){//仅适用于对象if(值类型==“对象”){//如果具有值,则返回对它的引用if(objMap.has(value))return objMap.get(value);objMap.set(值,`ref${objMap.size+1}`);}返回值;});返回字符串;}},参考解析:{value:函数(str){let parsed=JSON.parse(str);let objMap=_createObjectMap(已解析);objMap.forEach((value,key)=>_replaceKeyWithObject(value,key));返回解析;}},});//***************************示例设a={b: 32中,c:{获取a(){返回a;},获取c(){返回a.c;}}};let stringified=JSON.refStringify(a);let parsed=JSON.refParse(字符串化,2);console.log(已解析,JSON.refStringify(已解析));//***************************/示例//***************************助手函数_createObjectMap(obj){let objMap=new Map();JSON.stringify(obj,(key,value)=>{if(值类型==“对象”){if(objMap.has(value))return objMap.get(value);objMap.set(值,`ref${objMap.size+1}`);}返回值;});返回objMap;}函数_replaceKeyWithObject(key,obj,replaceWithObject=obj){Object.keys(obj).forEach(k=>{设val=obj[k];if(val==键)return(obj[k]=replaceWithObject);if(typeof val=='object'&&val!=replaceWithObject)_replaceKeyWithObject(key,val,replaceWithObject);});}

注意,还有一个由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”。