JavaScript object serializer concepts

How to support different serialization formats?
Supporting various formats requires serializer architecture to be pluggable. It should support replacing serializing engine and this can be done by separating common part from the algorithms specific to each engine. The specific engine contains two modules that support saving objects to stream and loading them. And the each module, either saver or loader, contains one part that represents a stream and another that represents one saved object in the stream.

Type-safe serialization
Proper deserialization requires type information of the saved objects and property values. The object type can be obtained directly from the object constructor or provided by some external data source. The writer can can save type information into the stream during serialization and load it during deserialization.

In simple cases, when constructor of the object is specified by a JavaScript function, like in the example below, it is possible to get type of the object from its .constructor property as follows:

function SampleObject()
{
	this.name = 'MySampleObject';
}

function getObjectType(object)
{
	var contructor = object.constructor.toString();
	var type = contructor.substr(8, contructor.indexOf('(') - 8).replace(/[\s\n\r]*/g, '');
	if ('' == type) {
		return null;
	}
	return type;
}

WScript.echo(getObjectType(new SampleObject()));
// Displays "SampleObject"

This code is based on the constructor special property of JavaScript objects, which contains the function the object is created with.

In more general case, the constructor may not have function name. This can be handled by using special array of registered constructors with their names, as follows:

function ClassRegistry()
{
	this._nameConstructorMap = {};
	this._constructorNameMap = {};
}

with (ClassRegistry)
{
	prototype.register = function (name, constr)
	{
		this._nameConstructorMap[name] = constr;
		this._constructorNameMap[constr] = name;
	};

	prototype.constr = function (name)
	{
		return this._nameConstructorMap[name];
	};

	prototype.name = function (arg)
	{
		if ('function' == arg) {
			return this._constructorNameMap[arg];
		}
		return this._constructorNameMap[arg.constructor];
	};

}

JavaScript is a weakly typed language, but it does not mean that it does not types at all. It only means that the variables can store value of any type but value itself always has some specific type. JavaScript has the following data types: Undefined, Null, Boolean, String, Number, and Object. If the property may contains values of different type, it should be saved in corresponded attribute during serialization. If property can contains only values of one type, it can be saved either in attribute or specified in property description.

<SampleObject1>
    <name type="string">MySampleObject</name>
    <id type="number">1</id>
    <active type="boolean">true</active>
</SampleObject1>

Serialization framework
As described, seven actors are participated in serialization process:

  • Serializer provides common interface and encapsulates general saving/loading algorithms.
  • IStorage is a serialization engine factory for IReader and IWriter.
  • IReader represents the stream Serializer reads data from by creating IObjectReader for each saved object.
  • And IWriter represents the stream Serializer writes data to. Together with IObjectWriter this is a mirrored pair of Readers.

Here is a detailed description of interfaces:

TODO