Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript Serialization of Typed Objects

I'm unclear how serialization/de-serialization is supposed to work on typed objects in JavaScript. For example, I have a "MapLayer" object that contains various members and arrays. I have written (but not yet tested) the following code to attempt to serialize it:

MapLayer.prototype.serialize = function() {
   var result = "{tileset:tilesets." + tilesets.getTilesetName(this.tileset) + ",columns:" + this.columns + ",rows:" + this.rows + 
   ",offsetX:" + this.offsetX + ",offsetY:" + this.offsetY + ",currentX:" + this.currentX + ",currentY:" + this.currentY + 
   ",scrollRateX:" + this.scrollRateX + ",scrollRateY:" + this.scrollRateY + ",virtualColumns:" + this.virtualColumns + ",virtualRows:" + this.virtualRows +
   ",tiles:\"" + this.encodeTileData2() + "\"";
   for(key in this)
   {
      if(this[key] instanceof Sprite)
         result += "," + key + ":" + this[key].serialize();
   }
   return result;
}

My question is, how is the resulting object supposed to get deserialized as a MapLayer object rather than as a generic Object. And how are all the Sprite instances supposed to get deserialized as sprites. Should I be using "new MapLayer()" instead of "{}"? Or am I simply supposed to include the prototype and constructor properties of the object in the serialization? Anything else I'm missing? Am I doing this a stupid way? There are 2 reasons I'm not using generic serialization/de-serialization code:

  1. I want to serialize the tile data in an optimized format rather than storing a base-10 string representation for each tile, and have them all delimited by commas.
  2. I don't want to serialize the tileset as an object that gets constructed as a new object during de-serialization, but rather as a reference to an existing object. Is that possible using code like I have proposed above?

Edit: Excuse my lack of proper terminology; JavaScript is one of my less expert languages. What I mean when I said "Typed Object" is an object with a constructor. In this example, my constructor is:

function MapLayer(map, tileset, columns, rows, virtualColumns, virtualRows, offsetX, offsetY, scrollRateX, scrollRateY, priority, tileData) {
   this.map = map;
   this.tileset = tileset;
   this.columns = columns;
   this.rows = rows;
   this.offsetX = offsetX;
   this.offsetY = offsetY;
   this.currentX = offsetX;
   this.currentY = offsetY;
   this.scrollRateX = scrollRateX;
   this.scrollRateY = scrollRateY;
   if(tileData.length < columns * rows * 2)
      this.tiles = DecodeData1(tileData);
   else
      this.tiles = DecodeData2(tileData);
   this.virtualColumns = virtualColumns ? virtualColumns : columns;
   this.virtualRows = virtualRows ? virtualRows : rows;
}

Edit 2: Taking the code from Šime Vidas' answer, I have added a related object called "Device":

function Device( description, memory ) {
    this.description = description;
    this.memory = memory;
}

function Person( name, sex, age, devices ) {
    this.name = name;
    this.sex = sex; 
    this.age = age;
    this.devices = devices;
}

Person.deserialize = function ( input ) {
    var obj = JSON.parse( input );
    return new Person( obj.name, obj.sex, obj.age, obj.devices );
};

var device = new Device( 'Blackberry', 64);
var device2 = new Device( 'Playstation 3', 600000);
var person = new Person( 'John', 'male', 25, [device, device2] );

var string = JSON.stringify(person);
console.log( string );

var person2 = Person.deserialize( string );
console.log( person2 );
console.log( person2 instanceof Person );

Now the question is how best to incorporate such dependent objects, because once again, the "type" (prototype?) of the object gets lost by JSON. Instead of running the constructor, why don't we simply change the serialize and the de-serialize functions to ensure that the object only needs to be constructed once like this instead of created and copied?

Person.prototype.serialize = function () {
    var obj = this; 
    return '({ ' + Object.getOwnPropertyNames( this ).map( function ( key ) {
        var value = obj[key];
        if ( typeof value === 'string' ) { value = '"' + value + '"'; }
        return key + ': ' + value;
    }).join( ', ' ) + ',"__proto__":Person.prototype})'; 
};

Person.deserialize = function ( input ) {
    return eval( input );
};

Edit 3: Another problem I have is that JSON doesn't seem to work in IE9. I'm using this test file:

<html>
<head>
<title>Script test</title>
<script language="javascript">
console.log(JSON);
</script>
</head>
</html>

And the console outputs:

SCRIPT5009: 'JSON' is undefined 
test.html, line 5 character 1

Edit 4: To correct the JSON problem I must include the correct DOCTYPE tag at the beginning.

like image 351
BlueMonkMN Avatar asked Dec 28 '11 14:12

BlueMonkMN


People also ask

Which type of objects can be serialized?

To serialize an object means to convert its state to a byte stream so way that the byte stream can be reverted back into a copy of the object. A Java object is serializable if its class or any of its superclasses implements either the java. io. Serializable interface or its subinterface, java.

Are JavaScript objects serializable?

In JavaScript, for example, you can serialize an object to a JSON string by calling the function JSON. stringify() . CSS values are serialized by calling the function CSSStyleDeclaration.

What is difference between Stringify and serialize?

stringify() ignores functions/methods when serializing. JSON also can't encode circular references. Most other serialization formats have this limitation as well but since JSON looks like javascript syntax some people assume it can do what javascript object literals can.

Which JavaScript method can be used to serialize an object into a string?

The convert() function uses keys() and map() method to convert the serialize object to query string.


2 Answers

For a start, here is a simple example of custom serialization / deserialization:

function Person( name, sex, age ) {
    this.name = name;
    this.sex = sex;
    this.age = age;
}

Person.prototype.serialize = function () {
    var obj = this;
    return '{ ' + Object.getOwnPropertyNames( this ).map( function ( key ) {
        var value = obj[key];
        if ( typeof value === 'string' ) { value = '"' + value + '"'; }
        return '"' + key + '": ' + value;
    }).join( ', ' ) + ' }';
};

Person.deserialize = function ( input ) {
    var obj = JSON.parse( input );
    return new Person( obj.name, obj.sex, obj.age );
};

Usage:

First, we create a new instance object:

var person = new Person( 'John', 'male', 25 );

Now, we serialize that object into a string using the Person.prototype.serialize method:

var string = person.serialize();

This will give use this string:

{ "name": "John", "sex": "male", "age": 25 }

Finally, we deserialize that string using the Person.deserialize static method:

var person2 = Person.deserialize( string );

Now, person2 is an instance of Person and contains the same property values as the original person instance.

Live demo: http://jsfiddle.net/VMqQN/


Now, while the Person.deserialize static method is required in any case (it uses JSON.parse internally, and invokes the Person constructor to initialize a new instance), the Person.prototype.serialize method on the other hand, is only needed if the built-in JSON.stringify static method doesn't suffice.

In my example above var string = JSON.stringify( person ) would get the job done too, so a custom serialization mechanism is not needed. See here: http://jsfiddle.net/VMqQN/1/ However, your case is more complex, so you'll need to define a custom serialization function.

like image 60
Šime Vidas Avatar answered Sep 28 '22 08:09

Šime Vidas


If you look at the ST-JS (http://st-js.org) project, it allows to create your object graph in Java on the server side, serialize it in JSON, and deserialize it on the client side (Javascript) in a typed manner, i.e. the objects will be instantiated using their constructor and you can call methods on the created objects.

To use ST-JS you should write your client code is Java, that is converted almost one-to-one in Javascript.

The AJAX/JSON chapter on the home page of the site explains you how to parse a JSON string while keeping the type information.

like image 31
alex.c Avatar answered Sep 28 '22 10:09

alex.c