2011-12-28 13 views
8

Nie jestem pewien, w jaki sposób serializacja/de-serializacja ma działać na wpisywanych obiektach w JavaScript. Na przykład, mam obiekt "MapLayer", który zawiera różne elementy i tablice. Pisałem (ale jeszcze nie testowane) następujący kod do próbują go serializacji:Serializacja Javascript typów maszyn

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

Moje pytanie brzmi, w jaki sposób powstały obiekt powinien dostać rozszeregować jako obiekt MapLayer raczej niż jako obiekt ogólny. I w jaki sposób wszystkie instancje Sprite powinny zostać spersjonalizowane jako sprite? Czy powinienem używać "nowego MapLayer()" zamiast "{}"? Czy powinienem po prostu włączyć prototyp i właściwości konstruktora obiektu do serializacji? Coś jeszcze, czego mi brakuje? Czy robię to głupio? Są 2 powody nie używam rodzajowy kod serializacji/de-serializacji:

  1. Chcę szeregować dane płytki zoptymalizowanym formacie zamiast przechowywania reprezentacji base-10 ciąg dla każdej płytki, i mieć je wszystkie rozdzielone przecinkami.
  2. Nie chcę serializować zestawu klocków jako obiektu, który zostanie skonstruowany jako nowy obiekt podczas deklinalizacji, ale raczej jako odniesienie do istniejącego obiektu. Czy jest to możliwe przy użyciu kodu, który zaproponowałem powyżej?

Edytuj: Przepraszam za brak właściwej terminologii; JavaScript jest jednym z moich mniej zaawansowanych języków. Co mam na myśli mówiąc "Typed Object" to obiekt z konstruktorem. W tym przykładzie, mój konstruktor jest:

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: Biorąc kod z odpowiedzią Sime Vidas', dodałem powiązany obiekt o nazwie "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); 

Teraz pytanie jak najlepiej włączyć takie zależne obiekty, ponieważ ponownie "typ" (prototyp?) obiektu zostanie utracony przez JSON. Zamiast uruchamiać konstruktora, dlaczego po prostu nie zmienimy funkcji przekształcania do postaci szeregowej i dekretyzacji, aby zapewnić, że obiekt będzie musiał zostać skonstruowany tylko raz, zamiast tworzyć i kopiować?

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: Innym problemem jest to, że mam JSON nie wydają się działać w IE9. Używam tego pliku testowego:

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

a wyjścia konsoli:

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

Edycja 4: Aby rozwiązać ten problem JSON muszę zawierać poprawny tag DOCTYPE na początku.

+1

Co to jest wpisany obiekt? –

+0

Obiekt z konstruktorem innym niż "Obiekt". Nie wiem, jak to nazwać. – BlueMonkMN

+0

Ach, masz na myśli obiekty będące instancjami własnych konstruktorów ('MapLayer',' Sprite', itp.) ... –

Odpowiedz

8

Na początek, oto prosty przykład zwyczaj serializacji/deserializacji:

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

Zastosowanie:

Najpierw tworzymy nową instancję obiektu:

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

Teraz serializujemy ten obiekt w łańcuchu przy użyciu metody Person.prototype.serialize:

var string = person.serialize(); 

To daje użyć tego ciągu:

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

Wreszcie, deserializowania ten ciąg używając Person.deserialize statyczną metodę:

var person2 = Person.deserialize(string); 

Teraz person2 jest instancją Person i zawiera tę samą właściwość wartości jako oryginalnej instancji person.

żywo demo:http://jsfiddle.net/VMqQN/


Teraz, gdy metoda statyczna Person.deserialize jest wymagane w każdym przypadku (używa JSON.parse wewnętrznie, i wywołuje konstruktor Person zainicjować nową instancję), metodę Person.prototype.serialize z drugiej strony jest potrzebny tylko wtedy, gdy wbudowana metoda statyczna JSON.stringify nie jest wystarczająca.

W powyższym przykładzie wykonanie zadania również wykonałby var string = JSON.stringify(person), więc niestandardowy mechanizm serializacji nie jest potrzebny. Zobacz tutaj: http://jsfiddle.net/VMqQN/1/ Jednak twoja sprawa jest bardziej złożona, więc musisz zdefiniować niestandardową funkcję serializacji.

+1

+1. ŚWIETNY Przykład. –

+0

Mam zaktualizowane pytanie. Dlaczego nie wywołujemy '__proto__' zamiast wywoływać konstruktora? – BlueMonkMN

+0

Innym problemem jest to, że jeśli spróbuję użyć JSON w IE, otrzymuję komunikat "JSON jest niezdefiniowany". – BlueMonkMN

1

Jeśli spojrzysz na projekt ST-JS (http://st-js.org), możesz stworzyć swój wykres obiektowy w Javie po stronie serwera, przekształcić go do postaci szeregowej w JSON i deserializować go po stronie klienta (Javascript) w typowy sposób , tzn. obiekty zostaną utworzone za pomocą ich konstruktora i można wywoływać metody na tworzonych obiektach.

Aby używać ST-JS, powinieneś napisać, że twój kod klienta to Java, który jest konwertowany prawie jeden do jednego w JavaScript.

Rozdział AJAX/JSON na stronie głównej witryny wyjaśnia, jak analizować łańcuch JSON, zachowując informacje o typie.

+0

Uważam, że OP pytał o techniki, które należy zastosować w ich istniejącej bazie kodu, a nie o inne ramy do migracji. –

+0

Tak, masz rację. Problem, który wskazywałem, polega na tym, że aby móc generalizować w sposób ogólny rodzaj wykresu "maszynopisanych" obiektów (jak w przypadku Edycji 2), trzeba w jakiś sposób przechowywać pewne metadane dotyczące "wpisanych obiektów", tj. twój deserializator, który na przykład "urządzenia" własności "Person" jest tablicą "Urządzenia". –

+0

Jeśli spojrzysz na nasze [źródła] (https://github.com/st-js/st-js/blob/master/generator/src/main/resources/stjs.js), funkcja parseJSON (ta powinna być w stanie łatwo wyodrębnić) wykorzystuje "statyczne" pole o nazwie "typeDescription", które jest prostą mapą - którą nasz framework generuje, ale można ją również utworzyć ręcznie. –