Chrome 发送请求错误:TypeError:将循环结构转换为 JSON

IT技术 javascript json google-chrome google-chrome-extension
2021-01-26 20:36:04

我有以下...

chrome.extension.sendRequest({
  req: "getDocument",
  docu: pagedoc,
  name: 'name'
}, function(response){
  var efjs = response.reply;
});

调用以下..

case "getBrowserForDocumentAttribute":
  alert("ZOMG HERE");
  sendResponse({
    reply: getBrowserForDocumentAttribute(request.docu,request.name)
  });
  break;

但是,我的代码从未到达“ZOMG HERE”,而是在运行时抛出以下错误 chrome.extension.sendRequest

 Uncaught TypeError: Converting circular structure to JSON
 chromeHidden.JSON.stringify
 chrome.Port.postMessage
 chrome.initExtension.chrome.extension.sendRequest
 suggestQuery

有谁知道是什么原因造成的?

6个回答

这意味着您在请求中传递的对象(我猜是pagedoc)具有循环引用,例如:

var a = {};
a.b = a;

JSON.stringify 不能转换这样的结构。

注意:DOM 节点就是这种情况,它们具有循环引用,即使它们没有附加到 DOM 树。在大多数情况下ownerDocument每个节点都有一个引用documentdocument具有通过至少一个参考DOM树document.bodydocument.body.ownerDocument指回document再次,这是唯一一个在DOM树中的多个循环引用。

@DougMolineux:当然,您可以try...catch用来捕获此错误。
2021-03-15 20:36:04
@FelixKling 不幸的是我无法让它工作(可能做错了什么)我最终使用了这个:github.com/isaacs/json-stringify-safe
2021-03-22 20:36:04
谢谢!这解释了我遇到的问题。但是 DOM 对象中存在的循环引用如何不会引起任何问题?JSON 会将document对象字符串化吗?
2021-03-28 20:36:04
@asgs:它确实会引起问题,至少在 Chrome 中是这样。Firefox 似乎更聪明一点,但我不知道它到底在做什么。
2021-03-31 20:36:04
是否有可能“捕捉”这个错误并处理它?
2021-04-08 20:36:04

根据Mozilla 的 JSON 文档JSON.stringify有第二个参数replacer可用于在解析树时过滤/忽略子项。但是,也许您可​​以避免循环引用。

在 Node.js 中我们不能。所以我们可以做这样的事情:

function censor(censor) {
  var i = 0;
  
  return function(key, value) {
    if(i !== 0 && typeof(censor) === 'object' && typeof(value) == 'object' && censor == value) 
      return '[Circular]'; 
    
    if(i >= 29) // seems to be a harded maximum of 30 serialized objects?
      return '[Unknown]';
    
    ++i; // so we know we aren't using the original object anymore
    
    return value;  
  }
}

var b = {foo: {bar: null}};

b.foo.bar = b;

console.log("Censoring: ", b);

console.log("Result: ", JSON.stringify(b, censor(b)));

结果:

Censoring:  { foo: { bar: [Circular] } }
Result: {"foo":{"bar":"[Circular]"}}

不幸的是,在它自动假定它是循环之前似乎最多有 30 次迭代。否则,这应该有效。我什areEquivalent 至从这里使用,但JSON.stringify在 30 次迭代后仍然抛出异常。尽管如此,如果您真的需要它,那么在顶层获得对象的体面表示就足够了。也许有人可以改进这一点?在 Node.js 的 HTTP 请求对象中,我得到:

{
"limit": null,
"size": 0,
"chunks": [],
"writable": true,
"readable": false,
"_events": {
    "pipe": [null, null],
    "error": [null]
},
"before": [null],
"after": [],
"response": {
    "output": [],
    "outputEncodings": [],
    "writable": true,
    "_last": false,
    "chunkedEncoding": false,
    "shouldKeepAlive": true,
    "useChunkedEncodingByDefault": true,
    "_hasBody": true,
    "_trailer": "",
    "finished": false,
    "socket": {
        "_handle": {
            "writeQueueSize": 0,
            "socket": "[Unknown]",
            "onread": "[Unknown]"
        },
        "_pendingWriteReqs": "[Unknown]",
        "_flags": "[Unknown]",
        "_connectQueueSize": "[Unknown]",
        "destroyed": "[Unknown]",
        "bytesRead": "[Unknown]",
        "bytesWritten": "[Unknown]",
        "allowHalfOpen": "[Unknown]",
        "writable": "[Unknown]",
        "readable": "[Unknown]",
        "server": "[Unknown]",
        "ondrain": "[Unknown]",
        "_idleTimeout": "[Unknown]",
        "_idleNext": "[Unknown]",
        "_idlePrev": "[Unknown]",
        "_idleStart": "[Unknown]",
        "_events": "[Unknown]",
        "ondata": "[Unknown]",
        "onend": "[Unknown]",
        "_httpMessage": "[Unknown]"
    },
    "connection": "[Unknown]",
    "_events": "[Unknown]",
    "_headers": "[Unknown]",
    "_headerNames": "[Unknown]",
    "_pipeCount": "[Unknown]"
},
"headers": "[Unknown]",
"target": "[Unknown]",
"_pipeCount": "[Unknown]",
"method": "[Unknown]",
"url": "[Unknown]",
"query": "[Unknown]",
"ended": "[Unknown]"
}

我在这里创建了一个小的 Node.js module来做到这一点:https : //github.com/ericmuyser/stringy随时改进/贡献!

这是我第一次看到传递的函数返回一个自执行函数,该函数返回一个常规函数。我相信我明白为什么要这样做,但我不相信我自己会找到那个解决方案,我觉得如果我能看到其他需要这种设置的例子,我会更好地记住这项技术话虽如此,您能否指出有关此设置/技术的任何文献(因为没有更好的词)或类似的文献?
2021-03-23 20:36:04
+1 给肖恩。请删除那个IEFE,它绝对没用且难以辨认。
2021-03-23 20:36:04
@BrunoLM:对于 30 次迭代限制,如果您返回,'[Unknown:' + typeof(value) + ']'您将看到如何修复审查器以正确处理函数和其他一些类型。
2021-03-25 20:36:04
我不知道为什么,但这个解决方案似乎对我不起作用。
2021-03-30 20:36:04
感谢指出审查员 arg!它允许调试循环问题。在我的情况下,我有一个 jquery 数组,我认为我有一个普通数组。它们在调试打印模式下看起来很相似。关于 IEFE,我看到它们经常用于绝对不需要它们的地方,并且同意 Shawn 和 Bergi 的观点,这就是这种情况。
2021-04-05 20:36:04

一种方法是从主对象中剥离对象和函数。并将更简单的形式字符串化

function simpleStringify (object){
    var simpleObject = {};
    for (var prop in object ){
        if (!object.hasOwnProperty(prop)){
            continue;
        }
        if (typeof(object[prop]) == 'object'){
            continue;
        }
        if (typeof(object[prop]) == 'function'){
            continue;
        }
        simpleObject[prop] = object[prop];
    }
    return JSON.stringify(simpleObject); // returns cleaned up JSON
};
对我来说完美的答案。也许'function'关键字错过了?
2021-03-20 20:36:04

我通常使用circular-json npm 包来解决这个问题。

// Felix Kling's example
var a = {};
a.b = a;
// load circular-json module
var CircularJSON = require('circular-json');
console.log(CircularJSON.stringify(a));
//result
{"b":"~"}

注意:circular-json 已被弃用,我现在使用 flatted(来自 CircularJSON 的创建者):

// ESM
import {parse, stringify} from 'flatted/esm';

// CJS
const {parse, stringify} = require('flatted/cjs');

const a = [{}];
a[0].a = a;
a.push(a);

stringify(a); // [["1","0"],{"a":"0"}]

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

tnx!。导入语法稍有改变。见这里github.com/WebReflection/flatted#readme
2021-03-11 20:36:04
该软件包已被弃用
2021-04-02 20:36:04

基于 zainengineer 的回答......另一种方法是制作对象的深层副本并去除循环引用并对结果进行字符串化。

function cleanStringify(object) {
    if (object && typeof object === 'object') {
        object = copyWithoutCircularReferences([object], object);
    }
    return JSON.stringify(object);

    function copyWithoutCircularReferences(references, object) {
        var cleanObject = {};
        Object.keys(object).forEach(function(key) {
            var value = object[key];
            if (value && typeof value === 'object') {
                if (references.indexOf(value) < 0) {
                    references.push(value);
                    cleanObject[key] = copyWithoutCircularReferences(references, value);
                    references.pop();
                } else {
                    cleanObject[key] = '###_Circular_###';
                }
            } else if (typeof value !== 'function') {
                cleanObject[key] = value;
            }
        });
        return cleanObject;
    }
}

// Example

var a = {
    name: "a"
};

var b = {
    name: "b"
};

b.a = a;
a.b = b;

console.log(cleanStringify(a));
console.log(cleanStringify(b));