Stringify (convertir en JSON) un objet JavaScript avec référence circulaire

j'ai une définition D'objet JavaScript qui contient une référence circulaire: il a une propriété qui fait référence à l'objet parent.

il a aussi des fonctions que je ne veux pas passer au serveur. Comment sérialiser et désérialiser ces objets?

j'ai lu que la meilleure méthode est D'utiliser le stringify de Douglas Crockford. Cependant, je reçois l'erreur suivante dans Chrome:

TypeError: conversion d'une structure circulaire en JSON

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

C'est un cas type que j'ai créé pour cette question. Il y a des erreurs à l'intérieur de ce code, mais j'ai essentiellement des objets à l'intérieur d'objets, et une référence passée à chaque objet pour montrer ce qu'est l'objet parent lorsque l'objet est créé. Chaque objet contient aussi des fonctions, que je ne veux pas stringifiées. Je veux juste les propriétés comme le Person.Name .

Comment puis-je sérialiser avant de l'envoyer au serveur et le desérialiser en supposant que le même JSON soit renvoyé?

55
demandé sur Luca Kiebel 2012-05-01 04:51:01

5 réponses

structure circulaire une erreur se produit lorsque vous possédez une propriété de l'objet qui est l'objet lui-même directement ( a -> a ) ou indirectement ( a -> b -> a ).

pour éviter le message d'erreur, prévenez JSON.stringify quoi faire lorsqu'il rencontre une référence circulaire. Par exemple, si vous avez une personne pointant vers une autre personne ("parent"), qui peut (ou non) pointer vers la personne originale, faites ce qui suit:

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

le deuxième paramètre de stringify est une fonction de filtre . Ici, il convertit simplement l'objet référencé en son ID, mais vous êtes libre de faire tout ce que vous voulez pour briser la référence circulaire.

vous pouvez tester le code ci-dessus avec le suivant:

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

BTW, je choisirais un nom d'attribut différent de " parent " car c'est un mot réservé dans de nombreuses langues (et en DOM). Cela tend à créer de la confusion en bas de la route...

95
répondu tocker 2017-08-28 10:43:00

il apparaît que dojo peut représenter des références circulaires dans JSON sous la forme: {"id":"1","me":{"$ref":"1"}}

voici un exemple:

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

produit:

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

Note: Vous pouvez également dé-sérialiser ces objets circulaires référencés en utilisant la méthode dojox.json.ref.fromJson .

Autres Ressources:

comment sérialiser le noeud DOM à JSON même s'il y a des références circulaires?

JSON.stringify ne peut pas représenter des références circulaires

9
répondu Brandon Boone 2017-05-23 10:31:30

j'ai trouvé deux modules appropriés pour gérer les références circulaires dans JSON.

  1. CircularJSON https://github.com/WebReflection/circular-json dont la sortie peut être utilisée comme entrée .analyser.)( Il fonctionne également dans les navigateurs & noeud.js Voir aussi: 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 qui peut-être plus lisible mais ne peut pas être utilisé pour .analyser et n'est disponible que pour le Nœud.js

L'un ou l'autre devrait répondre à vos besoins.

5
répondu nevf 2013-07-22 04:46:50

est arrivé sur ce thread parce que j'avais besoin d'enregistrer des objets complexes sur une page, car le débogage à distance n'était pas possible dans ma situation particulière. Trouvé propre cycle de Douglas Crockford (incepteur de JSON).js, qui annote les références circulaires en tant que chaînes de sorte qu'elles puissent être reconnectées après l'analyse. Le de-cycled deep copy est sûr pour passer par JSON.stringify. Profitez-en!

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

cycle.js: ce fichier contient deux fonctions, JSON.décycle et JSON.retrocycle, qui permet de coder des structures cycliques et dags en JSON, et de les récupérer ensuite. C'est une capacité qui n'est pas fourni par l'ES5. JSONPath est utilisé pour représenter les liens.

2
répondu Matt Evans 2017-03-30 20:20:53

j'ai utilisé ce qui suit pour éliminer les références circulaires:

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));
-11
répondu Timmerz 2014-02-28 19:41:46