Stringifizieren (in JSON konvertieren) eines JavaScript-Objekts mit Zirkelverweis

79

Ich habe eine JavaScript-Objektdefinition, die einen Zirkelverweis enthält: Sie hat eine Eigenschaft, die auf das übergeordnete Objekt verweist.

Es hat auch Funktionen, die ich nicht an den Server weitergeben möchte. Wie würde ich diese Objekte serialisieren und deserialisieren?

Ich habe gelesen, dass die beste Methode dazu die Verwendung von Douglas Crockfords Stringify ist. In Chrome wird jedoch der folgende Fehler angezeigt:

TypeError: Konvertieren einer Kreisstruktur in JSON

Der Code:

function finger(xid, xparent){
    this.id = xid;
    this.xparent;
    //other attributes
}

function arm(xid, xparent){
    this.id = xid;
    this.parent = xparent;
    this.fingers = [];

    //other attributes

    this.moveArm = function() {
        //moveArm function details - not included in this testcase
        alert("moveArm Executed");
    }
}

 function person(xid, xparent, xname){
    this.id = xid;
    this.parent = xparent;
    this.name = xname
    this.arms = []

    this.createArms = function () {
        this.arms[this.arms.length] = new arm(this.id, this);
    }
}

function group(xid, xparent){
    this.id = xid;
    this.parent = xparent;
    this.people = [];
    that = this;

    this.createPerson = function () {
        this.people[this.people.length] = new person(this.people.length, this, "someName");
        //other commands
    }

    this.saveGroup = function () {
        alert(JSON.stringify(that.people));
    }
}

Dies ist ein Testfall, den ich für diese Frage erstellt habe. Es gibt Fehler in diesem Code, aber im Wesentlichen habe ich Objekte in Objekten und eine Referenz, die an jedes Objekt übergeben wird, um zu zeigen, was das übergeordnete Objekt ist, wenn das Objekt erstellt wird. Jedes Objekt enthält auch Funktionen, die ich nicht stringifizieren möchte. Ich möchte nur die Eigenschaften wie die Person.Name.

Wie serialisiere ich vor dem Senden an den Server und deserialisiere ihn unter der Annahme, dass derselbe JSON zurückgegeben wird?

user1012500
quelle

Antworten:

113

Kreisstrukturfehler treten auf, wenn Sie eine Eigenschaft des Objekts haben, die das Objekt selbst direkt ( a -> a) oder indirekt ( a -> b -> a) ist.

Um die Fehlermeldung zu vermeiden, teilen Sie JSON.stringify mit, was zu tun ist, wenn ein Zirkelverweis auftritt. Wenn Sie beispielsweise eine Person haben, die auf eine andere Person ("Elternteil") zeigt, die möglicherweise auf die ursprüngliche Person verweist (oder nicht), gehen Sie wie folgt vor:

JSON.stringify( that.person, function( key, value) {
  if( key == 'parent') { return value.id;}
  else {return value;}
})

Der zweite Parameter stringifyist eine Filterfunktion . Hier konvertiert es einfach das referenzierte Objekt in seine ID, aber Sie können tun, was Sie möchten, um die Zirkelreferenz zu brechen.

Sie können den obigen Code folgendermaßen testen:

function Person( params) {
  this.id = params['id'];
  this.name = params['name']; 
  this.father = null;
  this.fingers = [];
  // etc.
}

var me = new Person({ id: 1, name: 'Luke'});
var him = new Person( { id:2, name: 'Darth Vader'});
me.father = him; 
JSON.stringify(me); // so far so good

him.father = me; // time travel assumed :-)
JSON.stringify(me); // "TypeError: Converting circular structure to JSON"
// But this should do the job:
JSON.stringify(me, function( key, value) {
  if(key == 'father') { 
    return value.id;
  } else {
    return value;
  };
});

Übrigens würde ich einen anderen Attributnamen als " parent" wählen, da es sich in vielen Sprachen (und in DOM) um ein reserviertes Wort handelt. Dies führt zu Verwirrung auf der Straße ...

tocker
quelle
Womit sollen wir die Variable ändern parent?
Esqarrouth
@Esqarrouth ownerwird normalerweise anstelle von verwendet parent.
DJDaveMark
10

Es scheint, dass Dojo Zirkelverweise in JSON in folgender Form darstellen kann:{"id":"1","me":{"$ref":"1"}}

Hier ist ein Beispiel:

http://jsfiddle.net/dumeG/

require(["dojox/json/ref"], function(){
    var me = {
        name:"Kris",
        father:{name:"Bill"},
        mother:{name:"Karen"}
    };
    me.father.wife = me.mother;
    var jsonMe = dojox.json.ref.toJson(me); // serialize me
    alert(jsonMe);
});​

Produziert:

{
   "name":"Kris",
   "father":{
     "name":"Bill",
     "wife":{
          "name":"Karen"
      }
   },
   "mother":{
     "$ref":"#father.wife"
   }
}

Hinweis: Mit dieser dojox.json.ref.fromJsonMethode können Sie diese Objekte, auf die mit Zirkel verwiesen wird, auch de-serialisieren .

Andere Ressourcen:

Wie serialisiere ich einen DOM-Knoten in JSON, auch wenn Zirkelverweise vorhanden sind?

JSON.stringify kann keine Zirkelverweise darstellen

Brandon Boone
quelle
Hallo, danke für deine Antwort. Ich hätte sagen sollen, dass ich jquery als meine is-Bibliothek verwende. Ich dachte nicht, dass es zu der Zeit relevant war. Ich werde meinen Beitrag aktualisieren.
user1012500
@ user1012500 - Dojo funktioniert neben jQuery einwandfrei. Ich füge oft andere Bibliotheken oder Frameworks hinzu, um Mängel in meinem primären Framework auszugleichen. Sie können sogar in der Lage sein , die zu extrahieren toJsonund fromJsonMethoden und um sie Ihren eigenen jQuery - Wrapper zu erstellen. Auf diese Weise müssen Sie nicht das gesamte Framework einbeziehen. Leider verfügt jQuery nicht über diese sofort einsatzbereite Funktionalität, und JSON.stringify kann diese Objekttypen nicht verarbeiten. Abgesehen von den obigen Beispielen müssen Sie diese Funktionalität möglicherweise selbst codieren.
Brandon Boone
1
Hallo Brandon, ich zögere, eine weitere Bibliothek hinzuzufügen, um das Problem zu lösen, da dadurch der Site ein weiterer Footprint hinzugefügt wird. Ich habe Dojo jedoch ausprobiert und versucht, Ihr Beispiel gegen mein Beispiel zu verwenden. Ich bin jedoch auf das Zirkelreferenzproblem
gestoßen
1
JSON, nicht zu verwechseln mit JavaScript-Objektliteralen, kann keine Funktionen enthalten, da es sich um ein Datenaustauschformat (wie XML) handelt. Um in das JSON-Format zu serialisieren, benötigen Sie ein kompatibles Objektliteral. In diesem Beispiel wird Ihr Beispiel so geändert, dass es kompatibel ist (obwohl es möglicherweise nicht das ist, wonach Sie suchen, da Sie nicht mehr mit Objektinstanzen arbeiten). Selbst wenn Sie Ihre toJSON-Funktionen entfernen, werden sie durch die Verweise auf die zugrunde liegenden Prototypfunktionen ungültig.
Brandon Boone
1
Das Ref-Modul von Dojo hat es geschafft, meine Modelle wie ein Kinderspiel zu fädeln / zu analysieren. In meinem Szenario war die Bibliothek cycle.js von crockford nicht erfolgreich, da ich newtonsoft json.net zum Serialisieren von C # -Objekten verwende und jsonpath nicht verwendet (wie von cycle.js verwendet). Sieht aus wie das gute alte Dojo meinen Tag gerettet hat! Danke für diese Antwort.
JoaoPauloPaschoal
5

Ich habe zwei geeignete Module gefunden, um Zirkelverweise in JSON zu verarbeiten.

  1. CircularJSON https://github.com/WebReflection/circular-json, dessen Ausgabe als Eingabe für .parse () verwendet werden kann. Es funktioniert auch in Browsers & Node.js. Siehe auch: http://webreflection.blogspot.com.au/2013/03/solving-cycles-recursions-and-circulars.html
  2. Isaacs json-stringify-safe https://github.com/isaacs/json-stringify-safe, das möglicherweise besser lesbar ist, aber nicht für .parse verwendet werden kann und nur für Node.js verfügbar ist

Beides sollte Ihren Anforderungen entsprechen.

nevf
quelle
4

Dies geschah in diesem Thread, weil ich komplexe Objekte auf einer Seite protokollieren musste, da Remote-Debugging in meiner speziellen Situation nicht möglich war. Es wurde Douglas Crockfords (Inceptor von JSON) eigenes cycle.js gefunden, das Zirkelverweise als Zeichenfolgen kommentiert, sodass sie nach dem Parsen wieder verbunden werden können. Die entzyklierte tiefe Kopie kann sicher durch JSON.stringify geleitet werden. Genießen!

https://github.com/douglascrockford/JSON-js

cycle.js: Diese Datei enthält zwei Funktionen, JSON.decycle und JSON.retrocycle, mit denen zyklische Strukturen und Dags in JSON codiert und anschließend wiederhergestellt werden können. Dies ist eine Funktion, die von ES5 nicht bereitgestellt wird. JSONPath wird verwendet, um die Links darzustellen.

Matt Evans
quelle
3

No-lib

Verwenden Sie den folgenden Ersetzer , um json mit Zeichenfolgenreferenzen (ähnlicher json-Pfad ) zu generieren , um Objekte zu duplizieren, auf die verwiesen wird

let s = JSON.stringify(obj, refReplacer());

Und folgende Parser-Funktion, um ein Objekt aus einem solchen "ref-json" neu zu generieren

Kamil Kiełczewski
quelle
-13

Ich habe Folgendes verwendet, um die Zirkelverweise zu entfernen:

JS.dropClasses = function(o) {

    for (var p in o) {
        if (o[p] instanceof jQuery || o[p] instanceof HTMLElement) {
            o[p] = null;
        }    
        else if (typeof o[p] == 'object' )
            JS.dropClasses(o[p]);
    }
};

JSON.stringify(JS.dropClasses(e));
Timmerz
quelle
3
Dadurch werden Instanzen von jQueryund HTMLElementnicht Zirkelverweise entfernt.
ZachB
1
@ZachB Ich denke, in seinem Setup sind diese für ihn kreisförmig ... aber das Problem ist, dass nur weil Javascript die verwendete Sprache ist, wir nicht jquery oder sogar HTML-Elemente haben.
Moeiscool