web-dev-qa-db-fra.com

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

J'ai une définition d'objet JavaScript qui contient une référence circulaire: elle a une propriété qui référence l'objet parent.

Il comporte également des fonctions que je ne souhaite pas transmettre au serveur. Comment pourrais-je sérialiser et désérialiser ces objets?

J'ai lu que la meilleure méthode consiste à utiliser le stringify de Douglas Crockford. Toutefois, l'erreur suivante s'affiche dans Chrome:

TypeError: Conversion de 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));
    }
}

Ceci est un test que j'ai créé pour cette question. Il y a des erreurs dans ce code, mais j'ai essentiellement des objets dans les objets et une référence transmise à chaque objet pour montrer ce qu'est l'objet parent lors de la création de l'objet. Chaque objet contient également des fonctions que je ne veux pas hiérarchiser. Je veux juste les propriétés telles que le Person.Name.

Comment puis-je sérialiser avant d'envoyer au serveur et le désérialiser en supposant que le même JSON est renvoyé?

66
user1012500

Structure circulaire Une erreur survient lorsque vous avez une propriété de l'objet qui est directement l'objet lui-même (a -> a) ou indirectement (a -> b -> a).

Pour éviter le message d'erreur, indiquez à JSON.stringify ce qu'il faut faire lorsqu'il rencontre une référence circulaire. Par exemple, si vous avez une personne qui pointe vers une autre personne ("parent"), qui peut (ou non) pointer vers la personne d'origine, procédez comme 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 ce que vous voulez pour casser la référence circulaire.

Vous pouvez tester le code ci-dessus avec les éléments suivants:

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

En passant, je choisirais un nom d'attribut différent de "parent" puisqu'il s'agit d'un mot réservé dans plusieurs langues (et dans le DOM). Cela tend à semer la confusion sur la route ...

103
tocker

Il semble que dojo puisse représenter des références circulaires en 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"
   }
}

Remarque: Vous pouvez également désérialiser ces objets circulaires référencés à l'aide de la commande dojox.json.ref.fromJson méthode.

Autres ressources:

Comment sérialiser un noeud DOM en JSON même s’il existe des références circulaires?

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

10
Brandon Boone

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

  1. CircularJSON https://github.com/WebReflection/circular-json dont la sortie peut être utilisée comme entrée pour .parse (). Cela fonctionne aussi dans Browsers & Node.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 est peut-être plus lisible mais ne peut pas être utilisé pour .parse et n'est disponible que pour Node.js

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

5
nevf

Cela s'est produit sur ce fil parce que je devais enregistrer des objets complexes sur une page, car le débogage à distance n'était pas possible dans ma situation particulière. Trouvé le propre cycle.js de Douglas Crockford (l'incepteur de JSON), qui annote les références circulaires sous forme de chaînes de sorte qu'elles puissent être reconnectées après l'analyse. La copie profonde décyclée est sécurisée pour passer par JSON.stringify. Prendre plaisir!

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

cycle.js: Ce fichier contient deux fonctions, JSON.decycle et JSON.retrocycle, qui permettent d’encoder des structures cycliques et des dags en JSON, puis de les récupérer. Cette fonctionnalité n’est pas fournie par ES5. JSONPath est utilisé pour représenter les liens.

2
Matt Evans