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

IT技术 javascript json node.js
2021-01-20 05:42:08

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

谢谢。

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

我想将 obj 字符串化为:

{"a":"foo"}
6个回答

在 Node.js 中,您可以使用util.inspect(object)它会自动用“[Circular]”替换圆形链接。


尽管是内置的(无需安装),但您必须导入它

import * as util from 'util' // has no default export
import { inspect } from 'util' // or directly
// or 
var util = require('util')
要使用它,只需调用
console.log(util.inspect(myObject))

另请注意,您可以传递选项对象进行检查(请参阅上面的链接)

inspect(myObject[, options: {showHidden, depth, colors, showProxy, ...moreOptions}])


请阅读下面的评论者并给他们点赞...

util 是一个内置module,您不必安装它。
2021-03-20 05:42:08
console.log(util.inspect(obj))
2021-03-23 05:42:08
不要像我一样 obj_str = util.inspect(thing)只是, NOT <s> garbage_str = JSON.stringify(util.inspect(thing))</s>
2021-03-31 05:42:08
@Mitar 它是内置的,但您仍然需要加载module var util = require('util');
2021-04-01 05:42:08
这比检查类型要好得多。为什么不能字符串化就这样工作?如果它知道有一个循环引用,为什么不能告诉它忽略它???
2021-04-07 05:42:08

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))
@user2451227“这个例子中的替换器不是 100% 正确的(取决于你对“重复”的定义。但这个概念是:使用自定义替换器,并跟踪解析的对象值。“
2021-03-16 05:42:08
这是错误的,因为它会跳过包含两次的对象的第二次出现,即使不是在真正的循环结构中。 var a={id:1}; JSON.stringify([a,a]);
2021-03-25 05:42:08
@AndriMöll 可以肯定,Rob 只是简洁地演示了自定义替换器,他做得很好。普雷斯顿的评论指出了一些Crockfordian 代码,如果这就是您所追求的,那么它是实弹健壮的。但概念是一样的。
2021-03-29 05:42:08
@CruzDiablo 序列化 DOM 通常毫无意义。但是,如果您可以为您的目的想到一个有意义的序列化方法,那么您可以尝试添加一个自定义序列化到 DOM 对象:(Node.prototype.toJSON = function() { return 'whatever you think that is right'; };如果您想要任何更通用/特定的东西,只需尝试原型树中的任何东西: HTMLDivElement 实现 HTMLElement 实现Element 实现 Node 实现 EventTarget;注意:这可能是浏览器相关的,前面的树对 Chrome 来说也是如此)
2021-03-30 05:42:08
这里的 GC 问题可以说是多余的。如果这作为单个脚本运行,则脚本立即终止。如果这被封装在一个函数中进行实现,那么cache将无法访问developer.mozilla.org/en-US/docs/Web/JavaScript/...
2021-04-08 05:42:08

我想知道为什么还没有人从 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());

看到的值应该存储在集合中,而不是数组中(替换器在每个元素上都被调用)并且不需要尝试导致循环引用的链中的JSON.stringify 每个元素

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

Yoda 说:“如果仍然支持 IE,那么应该使用转译器。”
2021-03-10 05:42:08
整洁,但这只是 ES2015。不支持 IE。
2021-03-17 05:42:08
replacer = () => { const seen = new WeakSet(); return (key, value) => { if (typeof value === "object" && value !== null) { if (seen.has(value)) { return; } seen.add(value); } return value; }; } () => { const seen = new WeakSet(); return (key, value) => { if (typeof value === "object" && value !== null) { if (seen.has(value)) { return; } seen.add(value); … JSON.stringify({a:1, b: '2'}, replacer)返回undefined
2021-03-27 05:42:08
我在stack.get is not a function使用 express 的 Response 对象时出错decycle来自github.com/douglascrockford/JSON-js/blob/master/cycle.js工作。
2021-04-03 05:42:08

做就是了

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是它的后继者。

请注意,“扁平化”(和循环 json?)包不会复制 JSON.stringify() 功能。它创建自己的非 JSON 格式。(例如,Flatted.stringify({blah: 1})结果[{"blah":1}])我看到有人试图就此提出问题,作者谴责他们并将问题锁定为评论。
2021-03-15 05:42:08
我认为您可能需要比“只是做”更多的理由来使用module。JSON原则上覆盖也不好
2021-03-18 05:42:08
根据作者的说法,该软件包已被弃用。CircularJSON 仅在维护中,flatted 是它的继任者。链接:github.com/WebReflection/flatted#flatted
2021-03-29 05:42:08
非常感谢!很棒的图书馆,节省了大量时间。超小(仅缩小 1.4KB)。
2021-04-02 05:42:08
我需要复制一个用于存根测试的对象。这个答案很完美。我复制了对象,然后删除了覆盖。谢谢!!
2021-04-07 05:42:08

我真的很喜欢 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);
};
// 浏览器不会打印超过 20K - 但是您将限制设置为 2k。或许为了未来而改变?
2021-03-25 05:42:08
你在这一行上缺少一个空检查:返回“(见”+(!!value.constructor?value.constructor.name.toLowerCase():typeof(value))+“带键”+printedObjectKeys[printedObjIndex]+ ")";
2021-03-30 05:42:08
我很乐意添加它。只是让我知道什么是可为空的,因为到目前为止我确实遇到了任何问题。
2021-04-03 05:42:08