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

谢谢

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

我想将对象字符串化为:

{"a":"foo"}

当前回答

使用带有自定义替换符的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))

其他回答

使用带有自定义替换符的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))

我在github上找到了循环json库,它很好地解决了我的问题。

我发现一些有用的好功能:

支持多平台使用,但到目前为止我只使用node.js测试了它。API是相同的,所以您需要做的就是将其包含并用作JSON替换。它有自己的解析方法,所以您可以将“循环”序列化数据转换回对象。

就这样吧

npm i --save circular-json

然后在js文件中

const CircularJSON = require('circular-json');
...
const json = CircularJSON.stringify(obj);

https://github.com/WebReflection/circular-json

注意:我与这个包裹无关。但我确实用它来做这个。

2020年更新

请注意,CircularJSON仅在维护中,flatted是其继任者。

我这样解决这个问题:

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

我建议从@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()来自定义净化行为。如果您发现自己需要一个简单的示例来说明如何做到这一点,我只是在这里编写了一个自定义替换器,它将错误、正则表达式和函数强制转换为人类可读的字符串。