Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
854 views
in Technique[技术] by (71.8m points)

json - 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...