Stephan
Stephan

Reputation: 3799

Ember model to json

I am looking for an efficient way to translate my Ember object to a json string, to use it in a websocket message below

/*
 * Model
 */

App.node = Ember.Object.extend({
  name: 'theName',
  type: 'theType',
  value: 'theValue',
})

The websocket method:

App.io.emit('node', {node: hash}); 

hash should be the json representation of the node. {name: thename, type: theType, ..} There must be a fast onliner to do this.. I dont want to do it manualy since i have many attributes and they are likely to change..

Upvotes: 25

Views: 25281

Answers (13)

akonovalov
akonovalov

Reputation: 351

And today in ember 5 it is serialize method that works the same as old toJSON method:

model.serialize({ includeId: true })

https://api.emberjs.com/ember-data/5.3/classes/Model/methods/serialize?anchor=serialize

@Esteban Borai answer helped me. Thanks!

Upvotes: 0

Esteban Borai
Esteban Borai

Reputation: 2509

Ember Data Model's object counts with a toJSON method which optionally receives an plain object with includeId property used to convert an Ember Data Model into a JSON with the properties of the model.

https://api.emberjs.com/ember-data/2.10/classes/DS.Model/methods/toJSON?anchor=toJSON

You can use it as follows:

const objects = models.map((model) => model.toJSON({ includeId: true }));

Hope it helps. Enjoy!

Upvotes: 1

ecairol
ecairol

Reputation: 6563

App.io.emit('node', {node: node.toJSON()});

Or if you have an ID property and want to include it:

App.io.emit('node', {node: node.toJSON({includeId: true})});

Upvotes: 3

Shimon Doodkin
Shimon Doodkin

Reputation: 4569

I have:

  • fixed and simplified code
  • added circular reference prevention
  • added use of get of value
  • removed all of the default properties of an empty component

    //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:""
    })
    */
    

Upvotes: 0

Klaus
Klaus

Reputation: 295

Here I take @leo, @pangratz and @kevin-pauli solution a little step further. Now it iterates not only with arrays but also through has many relationships, it doesn't check if a value has the type Array but it calls the isArray function defined in Ember's API.

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

Upvotes: 1

jennas
jennas

Reputation: 2464

I've written an extensive article on how you can convert ember models into native objects or JSON which may help you or others :)

http://pixelchild.com.au/post/44614363941/how-to-convert-ember-objects-to-json

http://byronsalau.com/blog/convert-ember-objects-to-json/

Upvotes: 2

Leo
Leo

Reputation: 56

I modified @Kevin-pauli solution to make it works with arrays as well:

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

I also made some further modification to get the best of both worlds. With the following version I check if the Jsonable object has a specific property that informs me on which of its properties should be serialized:

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

I am using this little tweak and I am happy with it. I hope it'll help others as well!

Thanks to @pangratz and @Kevin-Pauli for their solution!

Upvotes: 1

Kevin Pauli
Kevin Pauli

Reputation: 8885

I modifed @pangratz solution slightly to make it handle nested hierarchies of 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;
    }
});

Upvotes: 4

pangratz
pangratz

Reputation: 16143

As stated you can take inspiration from the ember-runtime/lib/core.js#inspect function to get the keys of an object, see 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);
    }
});

Note, since commit 1124005 - which is available in ember-latest.js and in the next release - you can pass the ret array directly to getProperties, so the return statement of the getJson function looks like this:

return this.getProperties(ret);

Upvotes: 14

mkoistinen
mkoistinen

Reputation: 7773

Will this work for you?

var json = JSON.stringify( Ember.getMeta( App.node, 'values') );

The false is optional, but would be more performant if you do not intend to modify any of the properties, which is the case according to your question. This works for me, but I am wary that Ember.meta is a private method and may work differently or not even be available in future releases. (Although, it isn't immediately clear to me if Ember.getMeta() is private). You can view it in its latest source form here:

https://github.com/emberjs/ember.js/blob/master/packages/ember-metal/lib/utils.js

The values property contains only 'normal' properties. You can collect any cached, computed properties from Ember.meta( App.node, false ).cached. So, provided you use jQuery with your build, you can easily merge these two objects like so:

$.extend( {}, Ember.getMeta(App.node, 'values'), Ember.getMeta(App.node, 'cache') );

Sadly, I haven't found a way to get sub-structures like array properties in this manner.

Upvotes: 2

Georg
Georg

Reputation: 113

I have also been struggling with this. As Mirko says, if you pass the ember object to JSON.stringify you will get circular reference error. However if you store the object inside one property and use stringify on that object, it works, even nested subproperties.

var node = Ember.Object.create({
  data: {
    name: 'theName',
    type: 'theType',
    value: 'theValue'
  }
});

console.log(JSON.stringify(node.get('data')));

However, this only works in Chrome, Safari and Firefox. In IE8 I get a stack overflow so this isn't a viable solution.

I have resorted to creating JSON schemas over my object models and written a recursive function to iterate over the objects using the properties in the schemas and then construct pure Javascript objects which I can then stringify and send to my server. I also use the schemas for validation so this solution works pretty well for me but if you have very large and dynamic data models this isn't possible. I'm also interested in simpler ways to accomplish this.

Upvotes: 4

ebryn
ebryn

Reputation: 4407

You can get a plain JS object (or hash) from an Ember.Object instance by calling getProperties() with a list of keys.

If you want it as a string, you can use JSON.stringify().

For example:

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"}'

Upvotes: 11

Topher Fangio
Topher Fangio

Reputation: 20667

Ember.js appears to have a JSON library available. I hopped into a console (Firebug) on one the Todos example and the following worked for me:

hash = { test:4 }
JSON.stringify(hash)

So you should be able to just change your line to

App.io.emit('node', { node:JSON.stringify(hash) })

Upvotes: -3

Related Questions