简体   繁体   English

Javascript 类型化对象的序列化

[英]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:我不清楚序列化/反序列化应该如何对 JavaScript 中的类型化对象起作用。例如,我有一个包含各种成员的“MapLayer”object 和 arrays。我已经编写(但尚未测试)以下代码以尝试序列化它:

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.我的问题是,如何将生成的 object 反序列化为 MapLayer object 而不是通用的 Object。以及如何将所有 Sprite 实例反序列化为精灵。 Should I be using "new MapLayer()" instead of "{}"?我应该使用“new MapLayer()”而不是“{}”吗? Or am I simply supposed to include the prototype and constructor properties of the object in the serialization?或者我只是应该在序列化中包含 object 的原型和构造函数属性? 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.我想以优化的格式序列化图块数据,而不是为每个图块存储以 10 为基数的字符串表示形式,并让它们全部用逗号分隔。
  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?我不想将 tileset 序列化为 object,它在反序列化期间构建为新的 object,而是作为对现有 object 的引用。是否可以使用我上面提出的代码?

Edit: Excuse my lack of proper terminology;编辑:请原谅我缺乏适当的术语; JavaScript is one of my less expert languages. JavaScript 是我不太专业的语言之一。 What I mean when I said "Typed Object" is an object with a constructor.当我说“类型化对象”时,我的意思是带有构造函数的 object。 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":编辑 2:从 Šime Vidas 的回答中获取代码,我添加了一个名为“设备”的相关 object:

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?现在的问题是如何最好地合并这些依赖对象,因为再一次,object 的“类型”(原型?)被 JSON 丢失了。为什么我们不运行构造函数,而是简单地更改序列化和 de - 序列化函数以确保object只需要像这样构造一次而不是创建和复制?

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.编辑 3:我遇到的另一个问题是 JSON 在 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.编辑 4:要更正 JSON 问题,我必须在开头包含正确的 DOCTYPE 标记。

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: 现在,我们使用Person.prototype.serialize方法将该对象序列化为一个字符串:

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: 最后,我们使用Person.deserialize静态方法反序列化该字符串:

var person2 = Person.deserialize( string );

Now, person2 is an instance of Person and contains the same property values as the original person instance. 现在, person2Person一个实例,包含与原始person实例相同的属性值。

Live demo: http://jsfiddle.net/VMqQN/ 现场演示: 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. 现在,虽然在任何情况下都需要Person.deserialize静态方法(它在内部使用JSON.parse ,并调用Person构造函数来初始化一个新实例),但另一方面,只需要Person.prototype.serialize方法。内置的JSON.stringify静态方法是不够的。

In my example above var string = JSON.stringify( person ) would get the job done too, so a custom serialization mechanism is not needed. 在上面的示例中, var string = JSON.stringify( person )也可以完成工作,因此不需要自定义序列化机制。 See here: http://jsfiddle.net/VMqQN/1/ However, your case is more complex, so you'll need to define a custom serialization function. 请参阅此处: http//jsfiddle.net/VMqQN/1/但是,您的情况更复杂,因此您需要定义自定义序列化功能。

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, ie the objects will be instantiated using their constructor and you can call methods on the created objects. 如果你看一下ST-JS( http://st-js.org )项目,它允许在服务器端用Java创建你的对象图,用JSON序列化它,并在客户端反序列化它(Javascript)以类型化的方式,即对象将使用其构造函数进行实例化,您可以在创建的对象上调用方法。

To use ST-JS you should write your client code is Java, that is converted almost one-to-one in Javascript. 要使用ST-JS,你应该编写你的客户端代码是Java,它在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. 该站点主页上的AJAX / JSON章节向您解释了如何在保留类型信息的同时解析JSON字符串。

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.我的问题是,如何将生成的 object 反序列化为 MapLayer object 而不是通用的 Object。以及如何将所有 Sprite 实例反序列化为精灵。

I've made an npm module named esserializer to solve this problem: save JavaScript class instance values during serialization, in plain JSON format, together with its class name information:我制作了一个名为esserializer的 npm 模块来解决这个问题:在序列化期间以普通 JSON 格式保存 JavaScript class 实例值及其 class 名称信息:

const ESSerializer = require('esserializer');
const serializedText = ESSerializer.serialize(anInstanceOfClassMapLayer);

Later on, during the deserialization stage (possibly on another machine), esserializer can recursively deserialize object instance, with all Class/Property/Method information retained, using the same class definition:稍后,在反序列化阶段(可能在另一台机器上),esserializer 可以递归反序列化 object 实例,保留所有类/属性/方法信息,使用相同的 class 定义:

const deserializedObj = ESSerializer.deserialize(serializedText, [MapLayer, Sprite]);
// deserializedObj is a perfect copy of anInstanceOfClassMapLayer

Should I be using "new MapLayer()" instead of "{}"?我应该使用“new MapLayer()”而不是“{}”吗? Or am I simply supposed to include the prototype and constructor properties of the object in the serialization?或者我只是应该在序列化中包含 object 的原型和构造函数属性?

Inside ESSerializer, class name information is included during serialization, and all prototype properties are re-constructed during deserialization.在ESSerializer内部,序列化时包含class名称信息,反序列化时重新构造所有原型属性。

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.我不想将 tileset 序列化为 object,它在反序列化期间被构造为新的 object,而是作为对现有 object 的引用。

Unfortunately, it is impossible.不幸的是,这是不可能的。 The serialization happens on one machine and the deserialization may happen on another computer -- anyway, this is what serialization/deserialization supposed to do.序列化发生在一台机器上,反序列化可能发生在另一台计算机上——无论如何,这就是序列化/反序列化应该做的。 Computer would never know "an existing object" on another machine.计算机永远不会知道另一台机器上的“现有对象”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM