Typed Deserialization with JSON

JSON is nice, but if you use it extensively, you go against the OO style of object modeling, which says that behaviors should be attached to objects themselves. Even if you are not an OO bigot, the OO way is sometimes just far more convenient and structures your code better, IMHO. Given my past work with JYaml, it can't be that hard to read in some JSON and directly deserialize it into a typed object can it?

Say you have a Person type:

function Person(name){
    this.name = name
}
Person.prototype.sayHi = function(){
    return "Hi! I am " + this.name
}
You create an instance called tommy:
> tommy = new Person('Tommy')
> tommy.sayHi()
Hi! I am Tommy
With a little magic from a toJson function that I whipped up:
> tommyJson = toJson(tommy)
> tommyJson
{__proto__: Person.prototype, name: "Tommy"}
tommyJson is now a string representation of tommy. You can see that it sets the special __proto__ link to Person.prototype, which will do its magic when it's deserialized. Now let's deserialize it back:
> tommyCopy = fromJson(tommyJson)> tommyCopy.sayHi()Hi! I am Tommy> tommyCopy.constructorfunction Person(name){    this.name = name;}> tommyCopy === tommyfalse
As you can see, the deserialized object tommyCopy is not just a plain object literal, but has all of the behaviors of Person.
The code for toJson and fromJson is so small I don't even bother putting it on github, so I'll just post it here(You may need ecma5array if you want to run it on a sucky browser):
function toJson(obj){
		    function keys(o){
		        var ret = []
		        for (var key in o){
		            var val = o[key]
		            if (o.hasOwnProperty(key)) // exclude attributes in the parent
		                ret.push(key)
		        }
		        return ret
		    }
		    function quote(str){
		        return '"' + str + '"'
		    }
		    if (obj === null || obj === undefined)
		        return String(obj)
		    else if (obj.constructor === String)
		        return quote(obj)
		    else if (obj.constructor === Array)
		        return '[' + obj.map(toJson).join(', ') + ']'
		    else if (obj.constructor === Object)
		        return '{' + keys(obj).map(function(key){return key + ': ' + toJson(obj[key])}).join(', ') + '}'
		    else if (obj.constructor && obj.constructor.name)
		        return '{' + ['__proto__: ' + obj.constructor.name + '.prototype'].concat(
		            keys(obj).map(function(key){return key + ': ' + toJson(obj[key])})).join(', ') + '}'
		    else
		        return String(obj)
		}
		function fromJson(str){
		    return eval('(' + str + ')')
		}
blog comments powered by Disqus