Сериализация JavaScript типизированных объектов

мне неясно, как сериализация / де-сериализация должна работать на типизированных объектах в JavaScript. Например, у меня есть объект" MapLayer", который содержит различные члены и массивы. Я написал (но еще не протестировал) следующий код, чтобы попытаться сериализовать его:

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;
}

мой вопрос в том, как результирующий объект должен десериализоваться как объект MapLayer, а не как общий объект. И как все экземпляры Sprite должны десериализоваться как спрайты. Должен ли я использовать "new MapLayer()" вместо "{}"? Или я просто должен включить свойства прототипа и конструктора объекта в сериализацию? Еще что-нибудь пропало? Я делаю это глупо? Есть 2 причины, по которым я не использую общий код сериализации / де-сериализации:

  1. Я хочу сериализовать данные плитки в оптимизированном формате, а не хранить строковое представление base-10 для каждой плитки, и все они разделены запятыми.
  2. Я не хочу сериализовать tileset как объект, который строится как новый объект во время де-сериализации, а скорее как ссылка на существующий объект. Возможно ли это использовать код, как я предложил выше?

Edit: извините за отсутствие правильной терминологии; JavaScript-один из моих менее опытных языков. Что я имею в виду, когда говорю "типизированный объект", это объект с конструктором. В этом примере, мой конструктор есть:

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: взяв код из ответа Šime Vidas, я добавил Связанный объект под названием "Устройство":

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 );

теперь вопрос в том, как лучше всего включить такие зависимые объекты, потому что еще раз "тип" (прототип?) объекта теряется JSON. Вместо запуска конструктора, почему бы нам просто не изменить функции serialize и de-serialize, чтобы гарантировать, что объект должен быть построен только один раз, как это вместо того, чтобы создавать и копировать?

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: Другая проблема заключается в том, что JSON, похоже, не работает в IE9. Я использую этот тестовый файл:

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

и в консоли выводит:

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

Edit 4: Чтобы исправить проблему JSON, я должен включить правильный тег DOCTYPE в начале.

2 ответов


для начала, вот простой пример пользовательской сериализации / десериализации:

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 );
};

использование:

во-первых, мы создаем новый экземпляр объекта:

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

теперь мы сериализуем этот объект в строку, используя Person.prototype.serialize способ:

var string = person.serialize();

это даст использовать эту строку:

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

наконец, мы десериализуем эту строку с помощью Person.deserialize статический метод:

var person2 = Person.deserialize( string );

теперь person2 - это пример Person и содержит те же значения свойств, как оригинальные person экземпляра.

демо: http://jsfiddle.net/VMqQN/


теперь, пока Person.deserialize статический метод необходим в любом случае (он использует JSON.parse внутренне, и вызывает Person конструктор для инициализации нового экземпляра), то Person.prototype.serialize метод с другой стороны, требуется, только если встроенный JSON.stringify статического метода недостаточно.

в моем примере выше var string = JSON.stringify( person ) также выполнит работу, поэтому пользовательский механизм сериализации не нужен. Смотрите здесь:http://jsfiddle.net/VMqQN/1/ однако ваш случай более сложный, поэтому вам нужно будет определить пользовательскую функцию сериализации.


Если вы посмотрите на ST-JS (http://st-js.org) проект, он позволяет создать граф объектов на Java на стороне сервера, сериализовать его в JSON и десериализовать его на стороне клиента (Javascript) типизированным способом, т. е. объекты будут созданы с помощью их конструктора, и вы можете вызывать методы на созданных объектах.

для использования ST-JS вы должны написать свой клиентский код Java, который преобразуется почти один к одному в Javascript.

в В главе AJAX/JSON на домашней странице сайта объясняется, как анализировать строку JSON, сохраняя информацию о типе.