如何以类似JSON的格式打印圆形结构?

JavaScript

米亚凯

2020-03-11

我有一个大对象,想要转换为JSON并发送。但是它具有圆形结构。我想扔掉任何存在的循环引用并发送任何可以字符串化的东西。我怎么做?

谢谢。

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

我想将obj字符串化为:

{"a":"foo"}

第771篇《如何以类似JSON的格式打印圆形结构?》来自Winter(https://github.com/aiyld/aiyld.github.io)的站点

8个回答
达蒙Tom 2020.03.11

Although this has been answered sufficiently, you could also explicitly delete the property in question before stringification using the delete operator.

delete obj.b; 
const jsonObject = JSON.stringify(obj);

delete operator

this will remove the need to build or maintain complex logic to remove circular references.

NearDavaid 2020.03.11

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);
飞云泡芙 2020.03.11

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

https://github.com/ericmuyser/stringy

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

逆天十三 2020.03.11

在github上发现了circular-json库,它可以很好地解决我的问题。

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

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

我知道这是一个古老的问题,但是我想建议我创建一个名为smart-circular的NPM软件包,该软件包的工作方式与其他建议的方式不同。如果您使用大而深的物体,则特别有用

一些功能是:

  • 用导致对象第一次出现的路径替换对象内的圆形引用或简单重复的结构(不仅仅是字符串[circular]);

  • 通过在广度优先搜索中查找圆度,程序包可确保此路径尽可能小,这在处理非常大和很深的对象时非常重要,在这种情况下,这些路径会变得很长且难以遵循( JSON.stringify做一个DFS);

  • 允许个性化替换,方便简化或忽略对象中次要的部分;

  • 最后,路径将按照访问引用字段所必需的方式精确编写,这可以帮助您进行调试。

猪猪JinJin西里 2020.03.11

对于将来的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);
}
Mandy村村 2020.03.11
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;
}
Tony宝儿 2020.03.11

我想知道为什么没有人从MDN页面发布正确的解决方案 ...

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 每个元素

就像在接受的答案中一样,此解决方案会删除所有重复值,而不仅仅是循环但是至少它没有指数复杂性。

问题类别

JavaScript Ckeditor Python Webpack TypeScript Vue.js React.js ExpressJS KoaJS CSS Node.js HTML Django 单元测试 PHP Asp.net jQuery Bootstrap IOS Android