Ошибка sendRequest в Chrome: TypeError: Преобразование циклической структуры в JSON
Я столкнулся с проблемой в коде, связанном с использованием chrome.extension.sendRequest
. У меня есть следующий код:
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
Кто-нибудь имеет представление о том, что вызывает эту ошибку?
5 ответ(ов)
Это означает, что объект, который вы передаете в запросе (предполагаю, это pagedoc
), имеет циклическую ссылку, что-то вроде следующего:
var a = {};
a.b = a;
JSON.stringify
не может преобразовать такие структуры.
Примечание. Это может быть связано с DOM-узлами, которые имеют циклические ссылки, даже если они не привязаны к дереву DOM. Каждый узел имеет свойство ownerDocument
, которое ссылается на document
в большинстве случаев. document
имеет ссылку на дерево DOM хотя бы через document.body
, а document.body.ownerDocument
снова ссылается на document
, что является только одной из множества циклических ссылок в дереве DOM.
Один из подходов заключается в том, чтобы удалить из основного объекта вложенные объекты и функции, оставив только простые значения, а затем преобразовать полученную упрощенную форму в строку. Вот пример функции, которая выполняет это:
function simpleStringify(object) {
const simpleObject = {};
for (const 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);
}
Если вы используете Node.js, можно также воспользоваться функцией inspect()
из модуля util
, чтобы вывести объект в удобном для чтения виде:
import { inspect } from "util";
console.log(inspect(object));
Этот метод более удобен для отладки и визуализации сложных объектов, сохраняя при этом информацию о их структуре.
Я обычно использую пакет circular-json
из npm для решения этой задачи.
// Пример от Феликса Клинга
var a = {};
a.b = a;
// Загружаем модуль circular-json
var CircularJSON = require('circular-json');
console.log(CircularJSON.stringify(a));
// результат
{"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"}]
Дополнительную информацию можно найти по следующей ссылке: flatted на npm.
Основываясь на ответе 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;
}
}
// Пример использования
var a = {
name: "a"
};
var b = {
name: "b"
};
b.a = a;
a.b = b;
console.log(cleanStringify(a));
console.log(cleanStringify(b));
В этом коде функция cleanStringify
проверяет, является ли входной объект действительным объектом. Если да, то она создает его глубокую копию с помощью вспомогательной функции copyWithoutCircularReferences
, которая удаляет циклические ссылки, добавляя значения в массив ссылок. Если циклическая ссылка обнаружена, вместо этого добавляется строка ###_Circular_###
. В конце, полученный очищенный объект сериализуется в JSON-строку.
В вашем случае вы просто забыли использовать async/await
при создании маршрута. Попробуйте воспользоваться следующим кодом:
app.get('/products', async (req, res) => {
const products = await Product.find();
res.send(products);
});
Обратите внимание на добавление async
перед функцией обратного вызова, чтобы корректно использовать await
для асинхронного получения данных о продуктах.
Доступ к переменным и функциям, определённым в контексте страницы, из расширения
Преобразование объекта JS в строку JSON
Как сравнить массивы в JavaScript?
Как прочитать JSON-файл в память сервера?
Поиск максимального значения свойства в массиве объектов