Chrome sendrequest error: TypeError: Konvertieren der Kreisstruktur in JSON

384

Ich habe folgendes ...

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

das ruft folgendes auf ..

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

Mein Code erreicht jedoch nie "ZOMG HERE", sondern löst beim Ausführen den folgenden Fehler aus chrome.extension.sendRequest

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

Hat jemand eine Ahnung, was das verursacht?

Skizit
quelle
2
Sie versuchen, ein Objekt mit Zirkelverweisen zu senden. Was ist pagedoc?
Felix Kling
9
Was meine ich mit was? 1. Was ist der Wert von pagedoc? 2. Rundschreiben:a = {}; a.b = a;
Felix Kling
1
Ahh .. das hat es behoben! Wenn Sie das in eine Antwort einfügen möchten, gebe ich Ihnen Anerkennung dafür!
Skizit
5
Versuchen Sie, node.js zu verwenden: util.inspect
boldnik

Antworten:

489

Dies bedeutet, dass das Objekt, das Sie in der Anfrage übergeben (ich denke es ist pagedoc), einen Zirkelverweis hat, etwa:

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

JSON.stringify Strukturen wie diese können nicht konvertiert werden.

NB : Dies wäre bei DOM-Knoten der Fall, die Zirkelverweise haben, auch wenn sie nicht an den DOM-Baum angehängt sind. Jeder Knoten hat eine ownerDocument, auf die documentin den meisten Fällen verwiesen wird. documenthat mindestens durch einen Verweis auf den DOM-Baum document.bodyund document.body.ownerDocumentverweist documenterneut auf diesen, der nur einer von mehreren Zirkelverweisen im DOM-Baum ist.

Felix Kling
quelle
2
Vielen Dank! Dies erklärt das Problem, das ich bekam. Aber wie verursacht der in den DOM-Objekten vorhandene Zirkelverweis keine Probleme? Würde JSON ein documentObjekt stringifizieren ?
Asgs
3
@asgs: Es tut Ursache Probleme, zumindest in Chrome. Firefox scheint ein bisschen schlauer zu sein, aber ich weiß nicht genau, was es tut.
Felix Kling
Ist es möglich, diesen Fehler zu "fangen" und zu behandeln?
Doug Molineux
2
@ DougMolineux: Sicher, Sie können try...catchdiesen Fehler abfangen.
Felix Kling
4
@ FelixKling Leider konnte ich das nicht zum Laufen bringen (könnte etwas falsch gemacht haben). Am Ende benutzte ich Folgendes: github.com/isaacs/json-stringify-safe
Doug Molineux
128

Gemäß den JSON - Dokumente bei Mozilla , JSON.Stringifyhat einen zweiten Parameter censor, die Filter verwendet werden kann / Kinder Elemente ignorieren , während die Baum Parsen. Vielleicht können Sie jedoch die Zirkelverweise vermeiden.

In Node.js können wir nicht. Also können wir so etwas machen:

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

Das Ergebnis:

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

Leider scheint es maximal 30 Iterationen zu geben, bevor automatisch angenommen wird, dass es kreisförmig ist. Andernfalls sollte dies funktionieren. Ich habe es sogar areEquivalent von hier aus verwendet , aber JSON.Stringifynach 30 Iterationen wird immer noch die Ausnahme ausgelöst. Trotzdem ist es gut genug, um eine anständige Darstellung des Objekts auf oberster Ebene zu erhalten, wenn Sie es wirklich brauchen. Vielleicht kann jemand das verbessern? In Node.js für ein HTTP-Anforderungsobjekt erhalte ich Folgendes:

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

Ich habe ein kleines Node.js-Modul erstellt, um dies hier zu tun: https://github.com/ericmuyser/stringy Fühlen Sie sich frei, sich zu verbessern / beizutragen!

Eric Muyser
quelle
10
Es ist das erste Mal, dass eine Funktion übergeben wird, die eine selbstausführende Funktion zurückgibt, die eine reguläre Funktion zurückgibt. Ich glaube, ich verstehe, warum dies getan wurde, aber ich glaube nicht, dass ich diese Lösung selbst gefunden hätte, und ich glaube, ich könnte mich besser an diese Technik erinnern , wenn ich andere Beispiele sehen könnte, in denen dieses Setup benötigt wird. Könnten Sie dennoch auf Literatur zu diesem Setup / dieser Technik (mangels eines besseren Wortes) oder ähnlichen verweisen?
Shawn
1
+1 an Shawn. Bitte entfernen Sie das IEFE, es ist absolut nutzlos und unleserlich.
Bergi
1
Danke für den Hinweis auf die Zensur arg! Es ermöglicht das Debuggen von zirkulären Problemen. In meinem Fall hatte ich ein JQuery-Array, in dem ich dachte, ich hätte ein normales Array. Beide sehen im Debug-Druckmodus ähnlich aus. In Bezug auf die IEFE sehe ich sie häufig an Orten verwendet, an denen sie absolut nicht benötigt werden, und stimme Shawn und Bergi zu, dass dies genau der Fall ist.
Citykid
1
Ich bin mir nicht sicher warum, aber diese Lösung scheint für mich nicht zu funktionieren.
Nikola Schou
1
@BrunoLM: Wenn Sie für 30 Iterationen zurückkehren, werden '[Unknown:' + typeof(value) + ']'Sie sehen, wie Sie den Zensor reparieren, um Funktionen und einige andere Typen richtig zu behandeln.
Alex Pakka
46

Ein Ansatz besteht darin, Objekte und Funktionen vom Hauptobjekt zu entfernen. Und stringifizieren Sie die einfachere 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
};
Zainengineer
quelle
2
Perfekte Antwort für mich. Möglicherweise wurde das Schlüsselwort "Funktion" übersehen?
Stepan Loginov
28

Normalerweise verwende ich das Circular-JSON-Npm-Paket, um dies zu lösen.

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

Hinweis: Circular-JSON ist veraltet, ich verwende jetzt Flatted (vom Ersteller von 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"}]

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

user3139574
quelle
8

Basierend auf der Antwort von zainengineer ... Ein anderer Ansatz besteht darin, eine tiefe Kopie des Objekts zu erstellen, Zirkelverweise zu entfernen und das Ergebnis zu stringifizieren.

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

CM
quelle
4

Ich löse dieses Problem auf NodeJS folgendermaßen:

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));
MiF
quelle
2

Beim Versuch, die folgende Nachricht mit jQuery zu erstellen, ist der gleiche Fehler aufgetreten. Der Zirkelverweis tritt auf, wenn reviewerNameer fälschlicherweise zugewiesen wurde msg.detail.reviewerName. JQuerys .val () hat das Problem behoben, siehe letzte Zeile.

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
Izilotti
quelle
1

Ich habe den gleichen Fehler mit jQuery formvaliadator erhalten, aber als ich ein console.log innerhalb der success: function entfernt habe, hat es funktioniert.

Azmeer
quelle
0

In meinem Fall wurde dieser Fehler angezeigt, als ich die asyncFunktion auf meiner Serverseite zum Abrufen von Dokumenten mit Mungo verwendete. Es stellte sich heraus, dass der Grund war, dass ich vergessen hatte, awaitvor dem Aufruf der find({})Methode zu setzen . Das Hinzufügen dieses Teils hat mein Problem behoben.

Mussa Charles
quelle
0

Dies funktioniert und zeigt Ihnen, welche Eigenschaften kreisförmig sind. Es ermöglicht auch die Rekonstruktion des Objekts mit den Referenzen

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

Beispiel mit viel entferntem Rauschen:

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

Um zu rekonstruieren, rufen Sie JSON.parse () auf und durchlaufen Sie die Eigenschaften, um nach dem [Circular Reference]Tag zu suchen . Dann hacke das ab und ... bewerte ... es mitthis set auf das Root-Objekt.

Bewerten Sie nichts, was gehackt werden kann. Besser wäre es, string.split('.')die Eigenschaften nach Namen zu suchen, um die Referenz festzulegen.

Derek Ziemba
quelle