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

谢谢

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

我想将对象字符串化为:

{"a":"foo"}

当前回答

本线程中的大多数答案都是专门针对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));

其他回答

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

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

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

@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;
  });
};

我建议从@isaacs中检查json字符串安全性——它在NPM中使用。

顺便说一下,如果你没有使用Node.js,你可以从源代码的相关部分复制并粘贴第4-27行。

要安装:

$ npm install json-stringify-safe --save

要使用:

// Require the thing
var stringify = require('json-stringify-safe');

// Take some nasty circular object
var theBigNasty = {
  a: "foo",
  b: theBigNasty
};

// Then clean it up a little bit
var sanitized = JSON.parse(stringify(theBigNasty));

这产生了:

{
  a: 'foo',
  b: '[Circular]'
}

注意,就像@Rob W提到的普通JSON.stringify函数一样,您也可以通过将“replacer”函数作为第二个参数传递给stringify()来自定义净化行为。如果您发现自己需要一个简单的示例来说明如何做到这一点,我只是在这里编写了一个自定义替换器,它将错误、正则表达式和函数强制转换为人类可读的字符串。

要更新重写JSON工作方式的答案(可能不推荐,但非常简单),不要使用循环JSON(已弃用)。相反,使用后续的,扁平的:

https://www.npmjs.com/package/flatted

借用了上面@user1541685的旧答案,但替换为新答案:

npm i—保存展平

然后在js文件中

const CircularJSON = require('flatted');
const json = CircularJSON.stringify(obj);
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;
}