Knock Me Out

Thoughts, ideas, and discussion about Knockout.js

Controlling How an Object Is Converted to JSON

| Comments

As a follow up to the post on Knockout utilities, I thought it might be useful to mention one less than obvious trick that you can use in conjunction with ko.toJSON.

When preparing your data to be sent back to the server, typically you will call ko.toJSON on your view model or at least part of your view model. This first calls ko.toJS to give you a clean copy of your objects with all observables unwrapped into plain properties. Then it converts this clean object into JSON using JSON.stringify, which is implemented natively by your browser (or with a library like json2.js for older browsers).

However, it is not uncommon for your objects to contain computed observables or other observables that aren’t meant to go back to the server. Maybe your server-side framework can’t handle the extra properties or you don’t want to wastefully send the extra bytes on the wire. In some cases you might even have a child with a reference to its parent, which creates a circular reference that causes problems in the conversion to JSON.

While you could walk your object tree and delete any unwanted properties, there is actually an easier way. JSON.stringify will check if each object has a toJSON method and execute it first before serializing the object to its JSON representation. The name of the function “toJSON” is a bit misleading, as the function should return an object that is ready to be serialized into JSON, not your own attempt at a JSON string representing the object.

Suppose that you are constructing a simple Person object that looks like:

1
2
3
4
5
6
7
function Person(first, last) {
    this.first = ko.observable(first);
    this.last = ko.observable(last);
    this.full = ko.dependentObservable(function() {
        return this.first() + " " + this.last();
    }, this);
}

When you call ko.toJSON on an object like this you will get:

1
{"first":"John","last":"Smith","full":"John Smith"}

Maybe your server-side code is not expecting the full property or you just want to avoid sending it to be efficient. To control which properties are actually serialized, you can implement a toJSON function for your object. It could look something like:

1
2
3
4
5
Person.prototype.toJSON = function() {
    var copy = ko.toJS(this); //easy way to get a clean copy
    delete copy.full; //remove an extra property
    return copy; //return the copy to be serialized
};

There are certainly many ways that you could implement this function, but the main objective is to return a version of the object that is ready to serialized. Now, your JSON would look like:

1
{"first":"John","last":"Smith"}

What if you would prefer to only send back the full name? Suppose that you have an array of Person objects and in JSON you want to convert it to an array of strings containing just the full names. In that case you could implement toJSON like:

1
2
3
Person.prototype.toJSON = function() {
   return ko.utils.unwrapObservable(this.full);
};

I used unwrapObservable, because if this happens to get called as part of ko.toJSON, then all of the observables would already be plain properties. This way we are flexible enough to handle it either way. If we had an array of Person objects, when converted to JSON they would look like:

1
["Ted Johnson","Jon Johnson","Rachel Johnson"]

Now, we have just an array of strings (no property names) to send to the server.

Here is a live sample:

Link to full sample on jsFiddle.net

Implementing the toJSON function for your objects can be a nice way to avoid manually mapping your view model. You can easily transform your data into whatever format is most suitable to be sent to your server.

Comments