javascript - ember serializers
Ember modelo a json (10)
Estoy buscando una manera eficiente de traducir mi objeto Ember a una cadena json, para usarlo en un mensaje websocket a continuación
/*
* Model
*/
App.node = Ember.Object.extend({
name: ''theName'',
type: ''theType'',
value: ''theValue'',
})
El método websocket:
App.io.emit(''node'', {node: hash});
hash debe ser la representación json del nodo. {name: thename, type: theType, ..} Debe haber una línea rápida para hacer esto ... No quiero hacerlo manualmente porque tengo muchos atributos y es probable que cambien.
Aquí tomo la solución @leo, @pangratz y @ kevin-pauli un paso más allá. Ahora itera no solo con matrices sino también a través de muchas relaciones, no verifica si un valor tiene el tipo Array pero llama a la función isArray definida en la API de Ember.
Coffeescript
App.Jsonable = Em.Mixin.create
getJson: ->
jsonValue = (attr) ->
return attr.map(jsonValue) if Em.isArray(attr)
return attr.getJson() if App.Jsonable.detect(attr)
attr
base =
if Em.isNone(@get(''jsonProperties''))
# no list given: let''s use all the properties
this
else
# the object has a selective list of properties to inspect
@getProperties(@get(''jsonProperties''))
hash = {}
for own key, value of base
continue if value is ''toString'' or Em.typeOf(value) is ''function''
json[key] = jsonValue(value)
json
Javascript
var hasProp = {}.hasOwnProperty;
App.Jsonable = Em.Mixin.create({
getJson: function() {
var base, hash, hashValue, key, value;
jsonValue = function(attr) {
if (Em.isArray(attr)) {
return attr.map(jsonValue);
}
if (App.Jsonable.detect(attr)) {
return attr.getJson();
}
return attr;
};
base = Em.isNone(this.get(''jsonProperties'')) ? this : this.getProperties(this.get(''jsonProperties''));
json = {};
for (key in base) {
if (!hasProp.call(base, key)) continue;
value = base[key];
if (value === ''toString'' || Em.typeOf(value) === ''function'') {
continue;
}
json[key] = jsonValue(value);
}
return json;
}
});
Como se indicó, puede inspirarse en la función ember-runtime/lib/core.js#inspect para obtener las claves de un objeto, consulte http://jsfiddle.net/pangratz666/UUusD/
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, ret = [];
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === ''toString'') {
continue;
} // ignore useless items
if (Ember.typeOf(v) === ''function'') {
continue;
}
ret.push(key);
}
}
return this.getProperties.apply(this, ret);
}
});
Tenga en cuenta que, dado que la confirmación 1124005 , que está disponible en ember-latest.js
y en la próxima versión, puede pasar la matriz de ret
directamente a getProperties
, por lo que la declaración de devolución de la función getJson
ve así:
return this.getProperties(ret);
Ember.js parece tener una biblioteca JSON disponible. Salté a una consola (Firebug) en uno de los ejemplos de Todos y lo siguiente funcionó para mí:
hash = { test:4 }
JSON.stringify(hash)
Entonces deberías poder cambiar tu línea a
App.io.emit(''node'', { node:JSON.stringify(hash) })
Funcionara para ti?
var json = JSON.stringify( Ember.getMeta( App.node, ''values'') );
Lo false
es opcional, pero sería más eficaz si no tiene la intención de modificar ninguna de las propiedades, que es el caso de acuerdo con su pregunta. Esto funciona para mí, pero desconfío de que Ember.meta sea un método privado y pueda funcionar de manera diferente o incluso no esté disponible en versiones futuras. (Aunque, no es inmediatamente claro para mí si Ember.getMeta () es privado). Puede verlo en su último formulario fuente aquí:
https://github.com/emberjs/ember.js/blob/master/packages/ember-metal/lib/utils.js
La propiedad values
contiene solo propiedades ''normales''. Puede recopilar cualquier propiedad calculada en caché de Ember.meta( App.node, false ).cached
. Entonces, siempre que use jQuery con su compilación, puede fusionar fácilmente estos dos objetos de la siguiente manera:
$.extend( {}, Ember.getMeta(App.node, ''values''), Ember.getMeta(App.node, ''cache'') );
Lamentablemente, no he encontrado una manera de obtener subestructuras como propiedades de matriz de esta manera.
He escrito un extenso artículo sobre cómo puedes convertir modelos de brasas en objetos nativos o JSON que pueden ayudarte a ti o a otros :)
http://pixelchild.com.au/post/44614363941/how-to-convert-ember-objects-to-json
Modifiqué la solución de @ Kevin-pauli para que funcione también con matrices:
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, json = {}, inspectArray = function (aSome) {
if (Ember.typeof(aSome) === ''array'') {
return aSome.map(inspectArray);
}
if (Jsonable.detect(aSome)) {
return aSome.getJson();
}
return aSome;
};
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === ''toString'') {
continue;
}
if (Ember.typeOf(v) === ''function'') {
continue;
}
if (Ember.typeOf(v) === ''array'') {
v = v.map(inspectArray);
}
if (App.Jsonable.detect(v))
v = v.getJson();
json[key] = v;
}
}
return json;
}
});
También hice algunas modificaciones adicionales para obtener lo mejor de ambos mundos. Con la siguiente versión, compruebo si el objeto Jsonable tiene una propiedad específica que me informa sobre cuáles de sus propiedades deben ser serializadas:
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, json = {}, base, inspectArray = function (aSome) {
if (Ember.typeof(aSome) === ''array'') {
return aSome.map(inspectArray);
}
if (Jsonable.detect(aSome)) {
return aSome.getJson();
}
return aSome;
};
if (!Ember.isNone(this.get(''jsonProperties''))) {
// the object has a selective list of properties to inspect
base = this.getProperties(this.get(''jsonProperties''));
} else {
// no list given: let''s use all the properties
base = this;
}
for (var key in base) {
if (base.hasOwnProperty(key)) {
v = base[key];
if (v === ''toString'') {
continue;
}
if (Ember.typeOf(v) === ''function'') {
continue;
}
if (Ember.typeOf(v) === ''array'') {
v = v.map(inspectArray);
}
if (App.Jsonable.detect(v))
v = v.getJson();
json[key] = v;
}
}
return json;
}
});
Estoy usando este pequeño truco y estoy contento con él. ¡Espero que ayude a otros también!
¡Gracias a @pangratz y @ Kevin-Pauli por su solución!
Modifiqué ligeramente la solución @pangratz para que maneje las jerarquías anidadas de Jsonables:
App.Jsonable = Ember.Mixin.create({
getJson: function() {
var v, json = {};
for (var key in this) {
if (this.hasOwnProperty(key)) {
v = this[key];
if (v === ''toString'') {
continue;
}
if (Ember.typeOf(v) === ''function'') {
continue;
}
if (App.Jsonable.detect(v))
v = v.getJson();
json[key] = v;
}
}
return json;
}
});
Puede obtener un objeto JS simple (o hash) de una instancia de Ember.Object
llamando a getProperties()
con una lista de claves.
Si lo quiere como una cadena, puede usar JSON.stringify()
.
Por ejemplo:
var obj = Ember.Object.create({firstName: ''Erik'', lastName: ''Bryn'', login: ''ebryn''}),
hash = obj.getProperties(''firstName'', ''lastName''), // => {firstName: ''Erik'', lastName: ''Bryn''}
stringHash = JSON.stringify(hash); // => ''{"firstName": "Erik", "lastName": "Bryn"}''
También he estado luchando con esto. Como dice Mirko, si pasas el objeto ascua a JSON.stringify obtendrás un error de referencia circular. Sin embargo, si almacena el objeto dentro de una propiedad y usa stringify en ese objeto, funciona, incluso subpropiedades anidadas.
var node = Ember.Object.create({
data: {
name: ''theName'',
type: ''theType'',
value: ''theValue''
}
});
console.log(JSON.stringify(node.get(''data'')));
Sin embargo, esto solo funciona en Chrome, Safari y Firefox. En IE8 obtengo un desbordamiento de pila así que esta no es una solución viable.
He recurrido a la creación de esquemas JSON sobre mis modelos de objetos y he escrito una función recursiva para iterar sobre los objetos usando las propiedades en los esquemas y luego construir objetos JavaScript puros que luego puedo codificar y enviar a mi servidor. También utilizo los esquemas para la validación, por lo que esta solución funciona bastante bien para mí, pero si tienes modelos de datos muy grandes y dinámicos, esto no es posible. También estoy interesado en formas más simples de lograr esto.
Yo tengo:
- código fijo y simplificado
- se agregó prevención circular de referencia
- uso agregado de obtener valor
eliminó todas las propiedades predeterminadas de un componente vacío
//Modified by Shimon Doodkin //Based on answers of: @leo, @pangratz, @kevin-pauli, @Klaus //http://.com/questions/8669340 App.Jsonable = Em.Mixin.create({ getJson : function (keysToSkip, visited) { //getJson() called with no arguments, // they are to pass on values during recursion. if (!keysToSkip) keysToSkip = Object.keys(Ember.Component.create()); if (!visited) visited = []; visited.push(this); var getIsFunction; var jsonValue = function (attr, key, obj) { if (Em.isArray(attr)) return attr.map(jsonValue); if (App.Jsonable.detect(attr)) return attr.getJson(keysToSkip, visited); return getIsFunction?obj.get(key):attr; }; var base; if (!Em.isNone(this.get(''jsonProperties''))) base = this.getProperties(this.get(''jsonProperties'')); else base = this; getIsFunction=Em.typeOf(base.get) === ''function''; var json = {}; var hasProp = Object.prototype.hasOwnProperty; for (var key in base) { if (!hasProp.call(base, key) || keysToSkip.indexOf(key) != -1) continue; var value = base[key]; // there are usual circular references // on keys: ownerView, controller, context === base if ( value === base || value === ''toString'' || Em.typeOf(value) === ''function'') continue; // optional, works also without this, // the rule above if value === base covers the usual case if (visited.indexOf(value) != -1) continue; json[key] = jsonValue(value, key, base); } visited.pop(); return json; } }); /* example: DeliveryInfoInput = Ember.Object.extend(App.Jsonable,{ jsonProperties: ["title","value","name"], //Optionally specify properties for json title:"", value:"", input:false, textarea:false, size:22, rows:"", name:"", hint:"" }) */