2012-01-08 9 views
27

json2.js parece ignorar los miembros del objeto principal al usar JSON.stringify(). Ejemplo:Cómo codificar los objetos heredados en JSON?

require('./json2.js'); 

function WorldObject(type) {  
    this.position = 4; 
} 

function Actor(val) { 
    this.someVal = 50; 
} 

Actor.prototype = new WorldObject(); 

var a = new Actor(2); 

console.log(a.position); 
console.log(JSON.stringify(a)); 

La salida es:

4 
{"someVal":50} 

Yo esperaría que esta salida:

4 
{"position":0, "someVal":50} 

Respuesta

31

Bueno, eso es sólo la forma en que está, JSON.stringify no conserva ninguna de las no- propiedades propias del objeto. Puede echar un vistazo a una discusión interesante sobre otros inconvenientes y posibles soluciones here.

También tenga en cuenta que el autor no solo ha documentado los problemas, sino que también ha escrito una biblioteca llamada HydrateJS que podría ayudarlo.

El problema es un poco más profundo de lo que parece a primera vista. Incluso si a realmente se aplica a {"position":0, "someVal":50}, luego analizarlo más tarde crearía un objeto que tiene las propiedades deseadas, pero no es una instancia de Actor, ni tiene un prototipo de enlace al WorldObject (después de todo, el método de análisis no lo hace tener esta información, por lo que no puede restaurarla de esa manera).

Para preservar la cadena de prototipos, se necesitan trucos inteligentes (como los utilizados en HydrateJS). Si esto no es lo que pretendes, tal vez solo necesites "aplanar" el objeto antes de atarlo. Para hacer eso, podría, por ejemplo, iterar todas las propiedades del objeto, independientemente de si son propias o no y reasignarlas (esto asegurará que se definan en el objeto en sí en lugar de heredarse del prototipo).

function flatten(obj) { 
    var result = Object.create(obj); 
    for(var key in result) { 
     result[key] = result[key]; 
    } 
    return result; 
} 

La forma en que se escribe la función no muta el objeto original. Así, utilizando

console.log(JSON.stringify(flatten(a))); 

que obtendrá la salida que desea y a seguirá siendo el mismo.

+0

Creo que este código es básicamente lo que necesito. Debería poder guardar esta versión serializada y escribir un cargador simple. ¡Buen trabajo! – wtjones

9

Marque esta violín: http://jsfiddle.net/AEGYG/

Puede plana-stringify el objeto de utilizar esta función:

function flatStringify(x) { 
    for(var i in x) { 
     if(!x.hasOwnProperty(i)) { 
      // weird as it might seem, this actually does the trick! - adds parent property to self 
      x[i] = x[i]; 
     } 
    } 
    return JSON.stringify(x); 
} 
+0

Ojalá pudiera marcar ambas como respuestas porque esto también funciona tan bien como el fragmento de Tomas, ¡gracias! – wtjones

20

Otra opción sería la de definir un método toJSON en el prototipo de objeto que desea serializar:

function Test(){} 

Test.prototype = { 

    someProperty: "some value", 

    toJSON: function() { 
     var tmp = {}; 

     for(var key in this) { 
      if(typeof this[key] !== 'function') 
       tmp[key] = this[key]; 
     } 

     return tmp; 
    } 
}; 

var t = new Test; 

JSON.stringify(t); // returns "{"someProperty" : "some value"}" 

Esto funciona desde JSON.stringify busca un método toJSON en el objeto que recibe, antes de intentar la na tiva de serialización.

1

Mientras que el enfoque flatten en general funciona, los fragmentos en otras respuestas publicadas hasta ahora no funcionan para las propiedades que no son modificables, por ejemplo, si el prototipo ha sido frozen. Para manejar este caso, necesitaría crear un nuevo objeto y asignar las propiedades a este nuevo objeto. Dado que solo está codificando el objeto resultante, la identidad del objeto y otras funciones internas de JavaScript probablemente no importen, por lo que está perfectamente bien devolver un objeto nuevo.Este enfoque también podría decirse que es más legible que la reasignación de propiedades de un objeto a sí mismo, ya que no se ve como un no-op:

function flatten(obj) { 
    var ret = {}; 
    for (var i in obj) { 
     ret[i] = obj[i]; 
    } 
    return ret; 
} 
1

Aquí es una versión recursiva del @TomasVana fragmento incluido en su respuesta, en caso no hay herencia en múltiples niveles de su árbol de objetos:

var flatten = function(obj) { 
    if (obj === null) { 
     return null; 
    } 

    if (Array.isArray(obj)) { 
     var newObj = []; 
     for (var i = 0; i < obj.length; i++) { 
      if (typeof obj[i] === 'object') { 
       newObj.push(flatten(obj[i])); 
      } 
      else { 
       newObj.push(obj[i]); 
      } 
     } 
     return newObj; 
    } 

    var result = Object.create(obj); 
    for(var key in result) { 
     if (typeof result[key] === 'object') { 
      result[key] = flatten(result[key]); 
     } 
     else { 
      result[key] = result[key]; 
     } 
    } 
    return result; 
} 

Y sigue matrices como matrices. Llámelo de la misma manera:

console.log(JSON.stringify(flatten(visualDataViews))); 
Cuestiones relacionadas