Chrome sendrequest error: TypeError: Converting circular structure to JSON
I've got the following...
chrome.extension.sendRequest({
req: "getDocument",
docu: pagedoc,
name: 'name'
}, function(response){
var efjs = response.reply;
});
which calls the following..
case "getBrowserForDocumentAttribute":
alert("ZOMG HERE");
sendResponse({
reply: getBrowserForDocumentAttribute(request.docu,request.name)
});
break;
However, my code never reaches "ZOMG HERE" but rather throws the following error while running chrome.extension.sendRequest
Uncaught TypeError: Converting circular structure to JSON
chromeHidden.JSON.stringify
chrome.Port.postMessage
chrome.initExtension.chrome.extension.sendRequest
suggestQuery
Does anyone have any idea what is causing this?
It means that the object you pass in the request (I guess it is pagedoc
) has a circular reference, something like:
var a = {};
a.b = a;
JSON.stringify
cannot convert structures like this.
N.B.: This would be the case with DOM nodes, which have circular references, even if they are not attached to the DOM tree. Each node has an ownerDocument
which refers to document
in most cases. document
has a reference to the DOM tree at least through document.body
and document.body.ownerDocument
refers back to document
again, which is only one of multiple circular references in the DOM tree.
As per the JSON docs at Mozilla, JSON.stringify
has a second parameter replacer
which can be used to filter/ignore children items while parsing the tree. However, perhaps you can avoid the circular references.
In Node.js we cannot. So we can do something like this:
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)));
The result:
Censoring: { foo: { bar: [Circular] } }
Result: {"foo":{"bar":"[Circular]"}}
Unfortunately there seems to be a maximum of 30 iterations before it automatically assumes it's circular. Otherwise, this should work. I even used areEquivalent
from here, but JSON.stringify
still throws the exception after 30 iterations. Still, it's good enough to get a decent representation of the object at a top level, if you really need it. Perhaps somebody can improve upon this though? In Node.js for an HTTP request object, I'm getting:
{
"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]"
}
I created a small Node.js module to do this here: https://github.com/ericmuyser/stringy Feel free to improve/contribute!
One approach is to strip object and functions from main object. And stringify the simpler form
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
};
I normally use the circular-json npm package to solve this.
// 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":"~"}
Note: circular-json has been deprecated, I now use flatted (from the creator of 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"}]
from: https://www.npmjs.com/package/flatted
Based on zainengineer's answer... Another approach is to make a deep copy of the object and strip circular references and stringify the result.
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));