2012-05-01 14 views
48

Tengo una definición de objeto JavaScript que contiene una referencia circular: tiene una propiedad que hace referencia al objeto principal.Stringify (convertir a JSON) un objeto JavaScript con referencia circular

También tiene funciones que no quiero que pasen al servidor. ¿Cómo serializaría y deserializaría estos objetos?

He leído que el mejor método para hacer esto es usar el stringify de Douglas Crockford. Sin embargo, estoy recibiendo el siguiente error en Chrome:

TypeError: Converting circular structure to JSON 

El código:

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

Se trata de un caso de prueba que he creado para esta pregunta. Hay errores dentro de este código, pero esencialmente tengo objetos dentro de los objetos y una referencia que se pasa a cada objeto para mostrar cuál es el objeto principal cuando se crea el objeto. Cada objeto también contiene funciones, que no quiero que se vuelvan cadenas. Solo quiero las propiedades como Person.Name.

¿Cómo serializo antes de enviar al servidor y deserializarlo suponiendo que se pasa el mismo JSON?

Respuesta

90

estructura circular error se produce cuando se tiene una propiedad del objeto que es el objeto en sí directamente (a -> a) o indirectamente (a -> b -> a) .

Para evitar el mensaje de error, indique a JSON.stringify qué hacer cuando encuentra una referencia circular. Por ejemplo, si usted tiene una persona que apunta a otra persona ("padre"), que puede (o no) apuntan a la persona original, haga lo siguiente:

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

El segundo parámetro a stringify es una función de filtro. Aquí simplemente convierte el objeto referido a su ID, pero puede hacer lo que quiera para romper la referencia circular.

puede probar el código anterior con el siguiente:

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

Por cierto, yo elegiría un nombre de atributo diferente a "parent" ya que es una palabra reservada en muchos idiomas (y en DOM). Esto tiende a causar confusión en el camino ...

9

Parece que dojo puede representar referencias circulares en JSON en la forma: {"id":"1","me":{"$ref":"1"}}

Aquí es un ejemplo:

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

Produce:

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

Nota: También puede deserializar estos objetos de referencia circular utilizando el método dojox.json.ref.fromJson.

Otros Recursos:

How to serialize DOM node to JSON even if there are circular references?

JSON.stringify can't represent circular references

+0

Hola, gracias por tu respuesta. Debería haber declarado que estoy usando jquery como mi biblioteca. No pensé que fuera relevante en ese momento. Actualizaré mi publicación. – user1012500

+0

@ user1012500 - Dojo funciona bien junto con jQuery. A menudo incluyo otras bibliotecas o marcos para compensar las deficiencias en mi marco principal. Incluso puede extraer los métodos 'toJson' y' fromJson' y crear su propia envoltura jQuery alrededor de ellos. De esa forma no necesitarás tirar de todo el marco. Desafortunadamente, jQuery no tiene esta funcionalidad lista para usar, y JSON.stringify no puede manejar este tipo de objetos. Por lo tanto, aparte de los ejemplos anteriores, es posible que deba codificar esta funcionalidad usted mismo. –

+1

Hola Brandon, dudo en agregar otra biblioteca para resolver el problema, ya que agrega otra huella al sitio. Sin embargo, le di una oportunidad al dojo e intenté usar tu ejemplo contra el mío. Sin embargo, me encontré con el problema de referencia circular (no tengo mucho conocimiento de Dojo, así que he intentado un par de cosas, pero principalmente basado en su ejemplo): http://jsfiddle.net/Af3d6/1/ – user1012500

4

Encontré dos módulos adecuados para manejar referencias circulares en JSON.

  1. CircularJSON https://github.com/WebReflection/circular-json cuya salida se puede utilizar como entrada para .parse(). También funciona en los navegadores & Node.js Ver también: http://webreflection.blogspot.com.au/2013/03/solving-cycles-recursions-and-circulars.html
  2. Isaacs JSON-stringify de fallos https://github.com/isaacs/json-stringify-safe la que tal vez sea más legible, pero no se puede utilizar para .parse y sólo está disponible para Node.js

Cualquiera de estos debe satisfacer sus necesidades.

-9

que utilizó la siguiente para eliminar las referencias circulares:

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

Esto eliminará instancias de 'jQuery' y' HTMLElement', no referencias circulares? – ZachB

+0

@ZachB Creo que en su configuración estos son circulares para él ... pero el problema es que el hecho de que javascript sea el idioma en uso no significa que tengamos elementos jquery o incluso HTML. – moeiscool

0

pasó a este hilo porque necesitaba registrar objetos complejos a una página, ya que la depuración remota no era posible en mi situación particular. Se encuentra el propio cycle.js de Douglas Crockford (inceptor de JSON), que anota las referencias circulares como cadenas de modo que puedan reconectarse después del análisis. La copia profunda eliminada del ciclo es segura para pasar a través de JSON.stringify. ¡Disfrutar!

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

cycle.js: Este archivo contiene dos funciones, JSON.decycle y JSON.retrocycle, que hacen posible codificar estructuras cíclicas y dags en JSON, y luego recuperarlos. Esta es una capacidad que no es proporcionada por ES5. JSONPath se utiliza para representar los enlaces.

Cuestiones relacionadas