Lỗi gửi dữ liệu Chrome: TypeError: Chuyển đổi cấu trúc vòng tròn sang JSON


384

Tôi đã có những điều sau đây ...

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

mà gọi như sau ..

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

Tuy nhiên, mã của tôi không bao giờ đạt đến "ZOMG TẠI ĐÂY" mà chỉ đưa ra lỗi sau khi chạy chrome.extension.sendRequest

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

Có ai có ý tưởng gì gây ra điều này?


2
Bạn đang cố gắng gửi một đối tượng có tham chiếu tròn trong đó. Là pagedoc
Felix Kling

9
Tôi có ý gì với cái gì? 1. Giá trị của là pagedocgì? 2. Tham chiếu thông tư:a = {}; a.b = a;
Felix Kling

1
À .. đã sửa rồi! Nếu bạn muốn đưa ra câu trả lời, tôi sẽ cung cấp cho bạn tín dụng cho nó!
Skizit

5
hãy thử sử dụng node.js: produc.inspect
boldnik

Câu trả lời:


489

Nó có nghĩa là đối tượng bạn chuyển trong yêu cầu (tôi đoán là vậy pagedoc) có một tham chiếu tròn, đại loại như:

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

JSON.stringify không thể chuyển đổi cấu trúc như thế này.

NB : Đây là trường hợp với các nút DOM, có tham chiếu vòng tròn, ngay cả khi chúng không được gắn vào cây DOM. Mỗi nút có một ownerDocumentđề cập đến documenttrong hầu hết các trường hợp. documentcó một tham chiếu đến cây DOM ít nhất thông qua document.bodydocument.body.ownerDocumentđề cập trở lại documentmột lần nữa, mà chỉ là một trong nhiều tham chiếu vòng tròn trong cây DOM.


2
Cảm ơn! Điều này giải thích vấn đề tôi nhận được. Nhưng làm thế nào để tham chiếu vòng tròn có trong các đối tượng DOM không gây ra vấn đề gì? JSON sẽ xâu chuỗi một documentđối tượng?
vào

3
@asgs: Nó không gây ra vấn đề, ít nhất là trong Chrome. Firefox có vẻ thông minh hơn một chút về nó, nhưng tôi không biết chính xác nó đang làm gì.
Felix Kling

Có thể "bắt" lỗi này và xử lý không?
Doug Molineux

2
@DougMolineux: Chắc chắn, bạn có thể sử dụng try...catchđể bắt lỗi này.
Felix Kling

4
@FelixKling Thật không may, tôi không thể làm việc đó (có thể đã làm gì đó sai) Tôi đã kết thúc bằng cách sử dụng này: github.com/isaacs/json-opesify-safe
Doug Molineux

128

Theo tài liệu JSON tại Mozilla , JSON.Stringifycó tham số thứ haicensor có thể được sử dụng để lọc / bỏ qua các mục con trong khi phân tích cú pháp cây. Tuy nhiên, có lẽ bạn có thể tránh các tài liệu tham khảo tròn.

Trong Node.js chúng ta không thể. Vì vậy, chúng ta có thể làm một cái gì đó như thế này:

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

Kết quả:

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

Thật không may, dường như có tối đa 30 lần lặp trước khi nó tự động giả định nó là thông tư. Nếu không, điều này sẽ làm việc. Tôi thậm chí đã sử dụng areEquivalent từ đây , nhưng JSON.Stringifyvẫn ném ngoại lệ sau 30 lần lặp. Tuy nhiên, nó đủ tốt để có được một đại diện tốt của đối tượng ở mức cao nhất, nếu bạn thực sự cần nó. Có lẽ ai đó có thể cải thiện điều này mặc dù? Trong Node.js cho một đối tượng yêu cầu HTTP, tôi nhận được:

{
"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]"
}

Tôi đã tạo một mô-đun Node.js nhỏ để thực hiện việc này tại đây: https://github.com/ericmuyser/opesy Hãy thoải mái cải thiện / đóng góp!


10
Đây là lần đầu tiên tôi thấy một chức năng được thông qua, nó trả về một chức năng tự thực thi, nó trả về một chức năng thông thường. Tôi tin rằng tôi hiểu tại sao điều này được thực hiện, nhưng tôi không tin rằng tôi sẽ tự mình tìm ra giải pháp đó và tôi cảm thấy mình có thể nhớ kỹ thuật này tốt hơn nếu tôi có thể xem các ví dụ khác khi cần thiết lập này. Điều đó đang được nói, bạn có thể chỉ ra bất kỳ tài liệu liên quan đến thiết lập / kỹ thuật này (vì thiếu một từ tốt hơn) hoặc những từ tương tự?
Shawn

1
+1 để Shawn. Vui lòng xóa IEFE, nó hoàn toàn vô dụng và không thể đọc được.
Bergi

1
thx đã chỉ ra người kiểm duyệt arg! nó cho phép gỡ lỗi xuống các vấn đề tròn. trong trường hợp của tôi, tôi đã có một mảng jquery trong đó tôi khó khăn hơn để có một mảng bình thường. cả hai đều trông giống nhau trong chế độ in gỡ lỗi. Về IEFE, tôi thấy chúng được sử dụng thường xuyên ở những nơi hoàn toàn không cần đến chúng và đồng ý với Shawn và Bergi rằng đây chỉ là trường hợp như vậy.
citykid

1
Tôi không chắc tại sao, nhưng giải pháp này dường như không hiệu quả với tôi.
Nikola Schou

1
@BrunoLM: trong giới hạn 30 lần lặp, nếu bạn quay lại, '[Unknown:' + typeof(value) + ']'bạn sẽ thấy cách sửa lỗi kiểm duyệt để xử lý đúng chức năng và một số loại khác.
Alex Pakka

46

Một cách tiếp cận là tước đối tượng và các chức năng khỏi đối tượng chính. Và xâu chuỗi các hình thức đơn giản hơn

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

2
Câu trả lời hoàn hảo cho tôi. Có lẽ từ khóa 'chức năng' bị bỏ lỡ?
Stepan Đăng nhập

28

Tôi thường sử dụng gói npm tròn-json để giải quyết điều này.

// 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":"~"}

Lưu ý: tròn-json đã không được chấp nhận, bây giờ tôi sử dụng flatted (từ người tạo ra Thông tư):

// 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"}]

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


8

Dựa trên câu trả lời của zainengineer ... Một cách tiếp cận khác là tạo một bản sao sâu của đối tượng và loại bỏ các tham chiếu vòng tròn và xâu chuỗi kết quả.

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



4

Tôi giải quyết vấn đề này trên NodeJS như thế này:

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

2

Tôi đã gặp lỗi tương tự khi cố gắng xây dựng thông báo bên dưới với jQuery. Tham chiếu vòng xảy ra khi reviewerNameđược gán nhầm msg.detail.reviewerName. .Query () của JQuery đã khắc phục sự cố, xem dòng cuối cùng.

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

1

Tôi đã nhận được cùng một lỗi với jQuery formvaliadator, nhưng khi tôi gỡ bỏ console.log bên trong thành công: function, nó đã hoạt động.


0

Đối với trường hợp của tôi, tôi đã gặp phải lỗi đó khi tôi đang sử dụng asyncchức năng ở phía máy chủ của mình để tìm nạp tài liệu bằng mongoose. Hóa ra lý do là tôi quên đặt awaittrước khi gọi find({})phương thức. Thêm phần đó đã khắc phục vấn đề của tôi.


0

Điều này hoạt động và cho bạn biết các thuộc tính là thông tư. Nó cũng cho phép tái cấu trúc đối tượng với các tham chiếu

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

Ví dụ với rất nhiều tiếng ồn được loại bỏ:

{
    "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"
    }
}

Để xây dựng lại cuộc gọi JSON.parse () sau đó lặp qua các thuộc tính tìm kiếm [Circular Reference]thẻ. Sau đó chặt nó ra và ... eval ... nó vớithis thiết lập cho đối tượng gốc.

Đừng đánh cắp bất cứ thứ gì có thể bị hack. Thực hành tốt hơn là làm string.split('.')sau đó tra cứu các thuộc tính theo tên để đặt tham chiếu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.