Ошибка отправки запроса Chrome: Ошибка типа: преобразование круговой структуры в 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
У кого-нибудь есть идеи, что вызывает это?
19 ответов
Это означает, что объект, который вы передаете в запросе (я думаю, это pagedoc
) имеет циклическую ссылку, что-то вроде:
var a = {};
a.b = a;
JSON.stringify
не может конвертировать такие структуры
NB: Это будет иметь место с узлами DOM, которые имеют циклические ссылки, даже если они не привязаны к дереву DOM. Каждый узел имеет ownerDocument
что относится к document
в большинстве случаев. document
имеет ссылку на дерево DOM по крайней мере через document.body
а также document.body.ownerDocument
относится к document
опять же, который является только одной из нескольких циклических ссылок в дереве DOM.
Согласно документам JSON в Mozilla, JSON.Stringify
имеет второй параметр censor
который может быть использован для фильтрации / игнорирования дочерних элементов при анализе дерева. Однако, возможно, вы можете избежать циклических ссылок.
В 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 стесняйтесь улучшать / вносить свой вклад!
Один из подходов состоит в том, чтобы отделить объект и функции от основного объекта. И зачеркните более простую форму
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
};
Я обычно использую пакет Round-JSON для решения этой проблемы.
// 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":"~"}
Основано на ответе 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/await при построении маршрута:
app.get('/products', async (req, res) => {
const products = await Product.find();
res.send(products );
});
Я решаю эту проблему на 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));
Это может быть не связанный ответ, но эта ссылка Обнаружение и исправление циклических ссылок в JavaScript может помочь обнаружить объекты, вызывающие циклическую зависимость.
Это работает и сообщает вам, какие свойства являются круговыми. Это также позволяет реконструировать объект по ссылкам
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]
тег. Затем отрежьте это и... оцените... с помощьюthis
установлен в корневой объект.
Не оценивайте ничего, что можно взломать. Лучшей практикой было бы сделатьstring.split('.')
затем найдите свойства по имени, чтобы установить ссылку.
В моем случае я получал эту ошибку, когда использовал async
на моем сервере для получения документов с помощью мангуста. Оказалось, что я забыл поставитьawait
перед звонком find({})
метод. Добавление этой части устранило мою проблему.
В моем случае я использую React Native и пытался отладить
console.log(JSON.stringify(object))
и получил ошибку:
TypeError: Converting circular structure to JSON
Кажется, что я могу зарегистрировать объект в консоли, используя просто:
console.log(object)
У меня возникла другая проблема: я брал значения из элементов html в массив объектов, в одном поле я неправильно назначал значения, что вызывало это исключение. Неправильное выражение:
obj.firstname=$("txFirstName")
Правильное выражение:
obj.firstname=$("txFirstName").val()
В моем случае это была функция flush() , которая осталась в модульном тесте после некоторых изменений кода.
До
it('something should be...', () => {
// do tests
flush();
}
После
it('something should be...', () => {
// do tests
}
Я столкнулся с той же ошибкой при попытке создать сообщение ниже с помощью jQuery. Циркулярная ссылка происходит, когда reviewerName
был по ошибке назначен msg.detail.reviewerName
,.Val () JQuery исправил проблему, смотрите последнюю строку.
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
Я получал ту же ошибку с jQuery formvaliadator, но когда я удалил console.log внутри success: function, это сработало.
Вы могли бы сделать что-то вроде этого
<Button onClick={fetchSuggestions}>
не осознавая, что вы передали «объект события» этой функции
если вы не хотите ничего передавать, просто отправьте пустую строку
<Button onClick={() => fetchSuggestions()}>
const fetchSuggestions = async (propsSession) => {
const {
error,
hasNextDoc,
suggestions: moreSuggestions,
} = await fetcher(`/admin/fetchSuggestion`, {
initialRequest: !!propsSession,
session: propsSession || session,
});
}
Для меня в объекте данных был элемент jquery или HTML как свойство «цель», которое содержит циклическую ссылку. Это свойство не требовалось для целей отправки объекта данных в формате JSON.
Удаление его путем удаления свойства устранило проблему:
if(__dataObj.hasOwnProperty('target')){
// remove target from data to avoid circular structure error
delete __dataObj.target;
}
Это круговой 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)
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"
}
Он думал, что
Я удалил