Je cherche un moyen efficace de traduire mon objet Ember en une chaîne json, de l'utiliser dans un message websocket ci-dessous
/*
* Model
*/
App.node = Ember.Object.extend({
name: 'theName',
type: 'theType',
value: 'theValue',
})
La méthode websocket:
App.io.emit('node', {node: hash});
le hachage doit être la représentation JSON du nœud. {name: thename, tapez: theType, ..} Il doit y avoir un onliner rapide pour le faire .. Je ne veux pas le faire manuellement car j'ai beaucoup d'attributs et ils sont susceptibles de changer ..
App.io.emit('node', {node: node.toJSON()});
Ou si vous avez une propriété ID et souhaitez l'inclure:
App.io.emit('node', {node: node.toJSON({includeId: true})});
Comme indiqué, vous pouvez vous inspirer de la fonction ember-runtime/lib/core.js # inspect pour obtenir les clés d’un objet, voir 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);
}
});
Notez que depuis commit 1124005 - qui est disponible dans ember-latest.js
et dans la prochaine version, vous pouvez passer le tableau ret
directement à getProperties
, de sorte que l'instruction de retour de la fonction getJson
ressemble à ceci:
return this.getProperties(ret);
Vous pouvez obtenir un objet JS simple (ou hachage) à partir d'une instance Ember.Object
en appelant getProperties()
avec une liste de clés.
Si vous le souhaitez sous forme de chaîne, vous pouvez utiliser JSON.stringify()
.
Par exemple:
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"}'
J'ai légèrement modifié la solution @pangratz pour lui faire gérer les hiérarchies imbriquées 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;
}
});
J'ai écrit un article détaillé sur la conversion des modèles de braises en objets natifs ou en JSON, ce qui peut vous aider ou aider d'autres personnes :)
http://pixelchild.com.au/post/44614363941/how-to-convert-ember-objects-to-json
J'ai aussi eu du mal avec ça. Comme dit Mirko, si vous passez l'objet ember à JSON.stringify, vous obtiendrez une erreur de référence circulaire. Toutefois, si vous stockez l'objet dans une propriété et utilisez stringify sur cet objet, cela fonctionne, même les sous-propriétés imbriquées.
var node = Ember.Object.create({
data: {
name: 'theName',
type: 'theType',
value: 'theValue'
}
});
console.log(JSON.stringify(node.get('data')));
Toutefois, cela ne fonctionne que dans Chrome, Safari et Firefox. Dans IE8, je reçois un débordement de pile, ce qui n’est pas une solution viable.
J'ai eu recours à la création de schémas JSON sur mes modèles d'objet et écrit une fonction récursive pour effectuer une itération sur les objets à l'aide des propriétés des schémas, puis construire des objets Javascript purs que je peux ensuite hiérarchiser et envoyer à mon serveur. J'utilise également les schémas pour la validation, donc cette solution fonctionne assez bien pour moi, mais si vous avez des modèles de données très volumineux et dynamiques, ce n'est pas possible. Je suis également intéressé par des moyens plus simples pour y parvenir.
Est-ce que cela fonctionnera pour vous?
var json = JSON.stringify( Ember.getMeta( App.node, 'values') );
La false
est facultative, mais serait plus performante si vous n'avez pas l'intention de modifier les propriétés, ce qui est le cas en fonction de votre question. Cela fonctionne pour moi, mais je me méfie du fait qu'Ember.meta est une méthode privée et peut fonctionner différemment ou même ne pas être disponible dans les prochaines versions. (Bien que ce ne soit pas immédiatement clair pour moi si Ember.getMeta () est privé). Vous pouvez le voir dans sa dernière forme source ici:
https://github.com/emberjs/ember.js/blob/master/packages/ember-metal/lib/utils.js
La propriété values
contient uniquement des propriétés "normales". Vous pouvez collecter toutes les propriétés calculées et mises en cache à partir de Ember.meta( App.node, false ).cached
. Donc, à condition d'utiliser jQuery avec votre construction, vous pouvez facilement fusionner ces deux objets comme suit:
$.extend( {}, Ember.getMeta(App.node, 'values'), Ember.getMeta(App.node, 'cache') );
Malheureusement, je n'ai pas trouvé le moyen d'obtenir de cette manière des sous-structures telles que les propriétés de tableau.
J'ai modifié la solution @ Kevin-pauli pour qu'elle fonctionne également avec les tableaux:
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;
}
});
J'ai également apporté quelques modifications supplémentaires pour obtenir le meilleur des deux mondes. Avec la version suivante, je vérifie si l’objet Jsonable a une propriété spécifique qui m’informe sur laquelle de ses propriétés doit être sérialisée:
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;
}
});
J'utilise ce petit Tweak et j'en suis heureux. J'espère que ça va aider les autres aussi!
Merci à @pangratz et @ Kevin-Pauli pour leur solution!
Ici, je prends un peu plus loin la solution @leo, @pangratz et @ kevin-pauli. Maintenant, il itère non seulement avec des tableaux, mais aussi via a beaucoup relations, il ne vérifie pas si une valeur a le type Array mais appelle la fonction isArray définie dans l'API d'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;
}
});
J'ai:
supprimé toutes les propriétés par défaut d'un composant vide
//Modified by Shimon Doodkin
//Based on answers of: @leo, @pangratz, @kevin-pauli, @Klaus
//http://stackoverflow.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:""
})
*/