How to: Convert Ember Objects to JSON - Byron Salau

Posted by | March 11, 2013 | javascript | One Comment

So you want to pass or ajax your Ember.Objects or Models to a JSON service. While it is possible, you might get some unexpected behavior and ill explain these to you, as well as the solutions I’m currently using.

Declaring your Models Correctly

It is best practice to declare your models properties in the extend method. For instance a contact model might look like the following:

//Contact Model
App.Contact = Ember.Object.extend({
Name: null,
Email: null,
Phone: null,
Fax: null
});

The Problem With This

Javascript is not strongly typed to start with but it might be acceptable to you that some properties are null-able for example “Fax” might not be required. Now Ember properties that are not set during create are moved into the object prototype. These null properties will appear invisible when you stringify to a JSON string. How annoying.

To demonstrate the problem, if we wanted to create a new contact with only Name and Email we might code it like this:

// Create our Object
var myContact = App.Contact.create({
Name: "Johny",
Email: "johny@gmail.com"
});

Then if we ajax it to a service using JSON.stringify()


// Post contact to service
$.ajax({
url: "/contact_service_url",
type: "POST",
data: {contact: JSON.stringify(myContact)}
});

Our service will only receive the following JSON result


{
"Name": "Johny",
"Email": "johny@gmail.com"
}

What the.. Where did our Phone and Fax properties go?

Im glad you asked. We did declare them, so we would expect them to serialize to JSON even though they are “null”, yes…. Me too.

However, because they were never set, they are left sitting in the prototype and do not serialize. I’m not sure why ember does this, but i can only assume it has to do with performance.

The Solution

To get around this I’ve come up with the following Ember mixin. The idea is to iterate over the object prototype and pick up all those missing properties. This also has the added benefit of returning a native object. Perfect.

App.Serializable = Ember.Mixin.create({
serialize: function ()
{
var result = {};
for (var key in $.extend(true, {}, this))
{
// Skip these
if (key === 'isInstance' ||
key === 'isDestroyed' ||
key === 'isDestroying' ||
key === 'concatenatedProperties' ||
typeof this[key] === 'function')
{
continue;
}
result[key] = this[key];
}
return result;
}
});

Usage

We can now modify our Contact Model and declare as serializable like so


//Contact Model that we can serialize
App.Contact = Ember.Object.extend(App.Serializable, {
Name: null,
Email: null,
Phone: null,
Fax: null
});

This time when we ajax the object we can serialize it first by adding .serialize()


// Post contact to service
$.ajax({
url: "/contact_service_url",
type: "POST",
data: {contact: JSON.stringify(myContact.serialize())}
});

And now our service will see ALL of the properties regardless if they have been set or not.


{
"Name": "Johny",
"Email": "johny@gmail.com",
"Phone": null,
"Fax": null
}

BEWARE ASP.NET developers with this gotchya

This mixin dosnt persist model property ordering. If you are using the inbuilt JavaScriptSerializer you will run into casting errors as it requires the properties to be in Alphabetical Order. I wont go into how stupid this is, but if you find a solution I would really appreciate it if you could share it with me.