Friday, 27 January 2017

Typed Serialization in Javascript

I had written some time ago about "typed serialization" in C# and Perl. What I mean is serializing an object with information about its type, and using that information later on to deserialize it into an instance of the correct type, rather than having that type "hardcoded" in code. I'll describe here one technique to achieve this in modern javascript.

The builtin JSON.stringify and JSON.parse are more than enough for my data serialization needs. Problem is, what about behaviour? JSON.parse will just create a plain object and add data to it, but we want our methods back! Assuming that the methods for your object are in its internal prototype [[Prototype]] (or further up in the prototype chain), and not directly attacched to the object itself, you need to set the [[Prototype]] of the new object accordingly. You can use for that Object.create or the more recent Object.setPrototypeOf.

So let's follow the whole procedure

First we have to serialize our data along with the type information. We'll just wrap our object in another object with that info, like this

 function serialize(obj){
  let aux = {
   typeName: obj.constructor.name,
   data: obj
  };
  return JSON.stringify(aux);
 }

When deserializing we'll have a string with the name of the "type" (the name of the constructor function for our object). To obtain the real function we can use the magic of eval:

function getFunctionFromStringName(functionNameSt){
 eval("var func = " + functionNameSt + ";");
 return func;
}

Once we have the function object, we can assign its prototype to our object, either this way:

//this does not work, Object.create takes a map of property descriptors
  //return Object.create(constructorFunc.prototype, aux.data);
  var obj = Object.create(constructorFunc.prototype);
  Object.assign(obj, aux.data);

Notice the comment. We can not just use the object with the data just deserialized because Object.create expects a bunch of property descriptors rather than simple data.

or this way:

 Object.setPrototypeOf(aux.data, constructorFunc.prototype);

I've put the code in a TypedSerializer class in this gist along with some test code.

No comments:

Post a Comment