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

我有以下内容…

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

有人知道是什么引起的吗?

897232 次浏览

这意味着你在请求中传递的对象(我猜它是pagedoc)有一个循环引用,类似于:

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

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

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

在尝试用jQuery构建下面的消息时,我也遇到过同样的错误。循环引用发生在reviewerName被错误地分配给msg.detail.reviewerName时。JQuery的.val()修复了这个问题,参见最后一行。

var reviewerName = $('reviewerName'); // <input type="text" id="taskName" />;
var msg = {"type":"A", "detail":{"managerReview":true} };
msg.detail.reviewerName = reviewerName; // Error
msg.detail.reviewerName = reviewerName.val(); // Fixed

根据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模块来做这个:https://github.com/ericmuyser/stringy请随意改进/贡献!

一种方法是从主对象中剥离对象和函数。并对更简单的形式进行stringalize

function simpleStringify (object){
// stringify an object, avoiding circular structures
// https://stackoverflow.com/a/31557814
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
};

我在NodeJS上是这样解决这个问题的:

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

我通常使用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":"~"}

注意:CircularJSON已弃用,我现在使用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"}]

来自:# EYZ0

我得到了同样的错误与jQuery formvaliadator,但当我删除了一个console.log内部的成功:功能,它工作。

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

对于我的情况,当我在服务器端使用async函数使用mongoose获取文档时,我得到了这个错误。事实证明,原因是我忘记在调用find({})方法之前放置await。添加这个部分解决了我的问题。

这可以工作并告诉您哪些属性是循环的。它还允许使用引用重新构造对象

  JSON.stringifyWithCircularRefs = (function() {
const refs = new Map();
const parents = [];
const path = ["this"];


function clear() {
refs.clear();
parents.length = 0;
path.length = 1;
}


function updateParents(key, value) {
var idx = parents.length - 1;
var prev = parents[idx];
if (prev[key] === value || idx === 0) {
path.push(key);
parents.push(value);
} else {
while (idx-- >= 0) {
prev = parents[idx];
if (prev[key] === value) {
idx += 2;
parents.length = idx;
path.length = idx;
--idx;
parents[idx] = value;
path[idx] = key;
break;
}
}
}
}


function checkCircular(key, value) {
if (value != null) {
if (typeof value === "object") {
if (key) { updateParents(key, value); }


let other = refs.get(value);
if (other) {
return '[Circular Reference]' + other;
} else {
refs.set(value, path.join('.'));
}
}
}
return value;
}


return function stringifyWithCircularRefs(obj, space) {
try {
parents.push(obj);
return JSON.stringify(obj, checkCircular, space);
} finally {
clear();
}
}
})();

删除大量噪音的例子:

{
"requestStartTime": "2020-05-22...",
"ws": {
"_events": {},
"readyState": 2,
"_closeTimer": {
"_idleTimeout": 30000,
"_idlePrev": {
"_idleNext": "[Circular Reference]this.ws._closeTimer",
"_idlePrev": "[Circular Reference]this.ws._closeTimer",
"expiry": 33764,
"id": -9007199254740987,
"msecs": 30000,
"priorityQueuePosition": 2
},
"_idleNext": "[Circular Reference]this.ws._closeTimer._idlePrev",
"_idleStart": 3764,
"_destroyed": false
},
"_closeCode": 1006,
"_extensions": {},
"_receiver": {
"_binaryType": "nodebuffer",
"_extensions": "[Circular Reference]this.ws._extensions",
},
"_sender": {
"_extensions": "[Circular Reference]this.ws._extensions",
"_socket": {
"_tlsOptions": {
"pipe": false,
"secureContext": {
"context": {},
"singleUse": true
},
},
"ssl": {
"_parent": {
"reading": true
},
"_secureContext": "[Circular Reference]this.ws._sender._socket._tlsOptions.secureContext",
"reading": true
}
},
"_firstFragment": true,
"_compress": false,
"_bufferedBytes": 0,
"_deflating": false,
"_queue": []
},
"_socket": "[Circular Reference]this.ws._sender._socket"
}
}

为了重建调用JSON.parse(),然后循环遍历属性,寻找[Circular Reference]标记。然后把它切下来…eval……将this设置为根对象。

不要评估任何可能被黑的东西。更好的做法是执行string.split('.'),然后按名称查找属性以设置引用。

Node.js v10.22.1(在我们的GitLab CI服务器上运行的版本)有一个我认为是错误的循环引用检测器。本地运行的版本(v12.8.0)足够智能,可以知道它不是真正的循环引用。

我添加这个响应是为了防止其他人有同样的问题,而他们的对象实际上不是循环引用。

这是原始的响应对象:

var res = {
"status":"OK",
"message":"Success",
"errCode":":",
"data":"",
"appCfg":{
"acp_age":"2yy",
"acp_us":"yes",
"mode":"admin",
"version":"v1.21.07.1"
},
"reqID":59833,
"email":{
"status":"OK",
"message":"Success"
},
"emailStatus":"sent"
}

它认为res.email.statusres.status是一样的。它只是一个文本元素,所以不是循环的,但是名称和值显然打乱了JSON。stringify解析器。

我删除了res.email子对象,一切正常。我试图从服务器调用期间执行的所有独特操作中收集独立状态和详细消息。我把它切换到元素res.emailStatus,它也包含在上面的例子中。

在我的情况下,我使用React Native,并尝试调试

console.log(JSON.stringify(object))

得到了错误:

TypeError: Converting circular structure to JSON

似乎我可以通过使用简单的方法将对象记录到控制台:

console.log(object)

在我的情况下,我只是忘记使用async/await的东西,而构建路由:

app.get('/products', async (req, res) => {
const products = await Product.find();
res.send(products );
});

我在这里遇到了一个不同的问题,我从html元素中获取值到对象数组中,在一个字段中,我错误地分配值,这导致了这个异常。 错误表达:obj.firstname=$("txFirstName") 正确表达式:obj.firstname=$("txFirstName").val()

在我的例子中,它是在一些代码更改后留在单元测试中的冲洗()

之前

it('something should be...', () => {
// do tests
flush();
}

it('something should be...', () => {
// do tests
}

你可能做过类似的事情

<Button onClick={fetchSuggestions}>

未能意识到您已将“事件对象”传递给该函数

如果你不想传递任何东西,只需发送一个空字符串

<Button onClick={() => fetchSuggestions()}>
  const fetchSuggestions = async (propsSession) => {
const {
error,
hasNextDoc,
suggestions: moreSuggestions,
} = await fetcher(`/admin/fetchSuggestion`, {
initialRequest: !!propsSession,
session: propsSession || session,
});
}

根据MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#issue_with_json.stringify_when_serializing_circular_references

它是一个循环json,不能直接转换。

解决方案1:

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

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


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


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


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

解决方案2:(同样通过MDN)

https://github.com/douglascrockford/JSON-js