Spis treści
Serializacja
Serializacja – w programowaniu proces przekształcania obiektów, tj. instancji określonych klas, do postaci szeregowej, czyli w strumień bajtów lub postać tekstową (np. XML, JSON) z zachowaniem aktualnego stanu obiektu. Serializowany obiekt może zostać utrwalony w pliku dyskowym, przesłany do innego procesu lub innego komputera poprzez sieć. Procesem odwrotnym do serializacji jest deserializacja. Proces ten polega na odczytaniu wcześniej zapisanego strumienia danych i odtworzeniu na tej podstawie obiektu klasy wraz z jego stanem bezpośrednio sprzed serializacji.
Serializacja służy do zapisu stanu obiektu, a później do odtworzenia jego stanu. Mechanizm ten jest używany między innymi na platformach .NET, Java, PHP, Python, Ruby.
Implementacje serializacji
[edytuj | edytuj kod]Serializacja na platformie .NET
[edytuj | edytuj kod]Istnieją dwa sposoby serializacji:
- poprzez użycie atrybutu Serializable
- poprzez implementację interfejsu ISerializable
Poniżej znajduje się przykład klasy w języku Delphi dla .NET, której obiekty mogą być serializowane:
type
[Serializable] // Obiekty tej klasy będą mogły być serializowane
TSerializableClass = class
FName: System.&String;
FValue: System.Int32;
[NonSerialized] // Nie serializuj poniższego pola
FNonSerialized: System.Int16;
end;
Poniżej znajduje się przykład w języku C# dla .NET, który serializuje przykład powyżej do pliku:
TSerializableClass Ts = new TSerializableClass();
//Tutaj powinien znajdować się fragment ustawiający wartości wybranych pól
BinaryFormatter binFormat = new BinaryFormatter(); // Tworzymy Formatter
Stream fStream = new FileStream("Przyklad.dat", FileMode.Create, FileAccess.Write, FileShare.None); //tworzymy strumień z prawami do utworzenia nowego pliku
binFormat.Serialize(fStream, Ts); // serializacja naszej klasy do pliku Przyklad.dat
fStream.Close(); // zamknięcie strumienia
Klasa zawiera trzy pola, przy czym jedno z nich – FNonSerialized
– nie będzie serializowane (wskazuje na to atrybut NonSerialized).
Środowisko .NET oferuje trzy podstawowe formaty zapisu (formatery) serializowanych klas: binarny, SOAP oraz XML.
Środowisko uruchomieniowe CLR podczas procesu serializacji tworzy graf obiektu. Każdy obiekt posiada swój identyfikator. Wszystkie obiekty odwołujące się do serializowanego obiektu są wiązane z obiektem głównym.
Formatery
[edytuj | edytuj kod]Zadaniem formatera jest konwersja obiektu do formatu, w którym zostanie zserializowany obiekt, lub z którego zostanie zdeserializowany. Platforma .NET udostępnia trzy podstawowe formatery: binarny (obiekt zostanie zapisany jako ciąg zero-jedynkowy; formater zdefiniowany w przestrzeni nazw, System.Runtime.Serialization.Formatters.Binary), SOAP (obiekt zostanie zapisany w formacie przypominającym format XML; formater zdefiniowany w przestrzeni nazw System.Runtime.Serialization.Formatters.SOAP), XML (obiekt zostanie zapisany w formacie XML; formater zdefiniowany w przestrzeni nazw System.Xml.Serialization.XmlSerializer). System formaterów jest rozszerzalny.
Różnice między rodzajami serializacji
[edytuj | edytuj kod]- Binary – bardzo prosty sposób zapisu obiektu w strumień. Zaletą jest prostota użycia (wymaga atrybutu [Serializable] + strumień + formatter). Wady – oprócz standardowych informacji, zapisywane są też metadane dotyczące aktualnej wersji platformy .NET oraz specyfikacja typów (zgodna z aktualną wersją platformy).
- SOAP – bardzo prosty sposób zapisu obiektu w strumień. Plik wynikowy przypomina swoją strukturą plik XML, jednak jest on wstępnie formatowany przez platformę .NET, dzięki czemu uzyskujemy prostotę użycia identyczną jak w przypadku BinaryFormatera. Dodatkowym atutem jest brak niepotrzebnych metadanych, dzięki czemu zapis typu SOAP jest w pełni niezależny od platformy. Wada – tak jak w przypadku języka XML serializowany obiekt powinien posiadać „pusty” konstruktor i składać się z publicznie dostępnych pól (pole albo samo powinno być publiczne [public] albo posiadać publiczną [public] właściwość).
- XML – serializacja w pełni zgodna ze standardami języka XML. Każde serializowane pole powinno być public (lub mieć publiczną właściwość), wymagany jest „pusty” konstruktor, dodatkowo zarówno podczas serializacji jak i deserializacji należy podać pełną strukturę pliku XML.
Serializacja w języku Java
[edytuj | edytuj kod]By obiekt mógł być serializowany, musi implementować interfejs Serializable. Dla wygody sporo standardowych klas Javy implementuje ten interfejs, nie ma więc potrzeby wyprowadzać np. własnego obiektu będącego dzieckiem klasy Vector.
W przypadku serializacji obiektu, który agreguje inne obiekty, serializacji ulegnie cała hierarchia obiektów, jednak każdy z nich musi implementować interfejs Serializable. Przykładem może być serializacja wyżej wspomnianego wektora.
Przykład:
package test;
import java.io.Serializable;
public class Account implements Serializable {
private String surname = null;
private String firstname = null;
public Account (String surname, String firstname) {
this.surname = surname;
this.firstname = firstname;
}
public String getFirstname() {
return this.firstname;
}
public String getSurname() {
return this.surname;
}
}
Interfejs Serializable nie wymaga implementacji żadnej metody. Każdy obiekt, który zaimplementował interfejs Serializable, użytkownik może serializować/deserializować do/ze strumienia. Dla powyższego przykładu i serializacji do pliku o nazwie test.ser mogłoby to wyglądać tak jak poniżej:
Account a = new Account("Jan","Nowak");
FileOutputStream fos = null;
ObjectOutputStream oos = null;
/*
* Zapis do strumienia (plikowego, ale może być dowolne)
*/
try {
fos= new FileOutputStream("test.ser"); //utworzenie strumienia wyjściowego
oos = new ObjectOutputStream(fos); //utworzenie obiektu zapisującego do strumienia
oos.writeObject(a); //serializacja obiektu
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// zamykamy strumienie w finally
try {
if (oos != null) oos.close();
} catch (IOException e) {}
try {
if (fos != null) fos.close();
} catch (IOException e) {}
}
Odczytuje się tak samo, ale używa obiektów odczytu, a nie zapisu:
Account b = null;
/*
* Odczyt ze strumienia plikowego (ale może być dowolne)
*/
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream("test.ser"); //utworzenie strumienia wejściowego
ois = new ObjectInputStream(fis); //utworzenie obiektu odczytującego obiekty ze strumienia
b = (Account) ois.readObject(); //deserializacja obiektu
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
// zasoby zwalniamy w finally
try {
if (ois != null) ois.close();
} catch (IOException e) {}
try {
if (fis != null) fis.close();
} catch (IOException e) {}
}
Serializacja w języku Python
[edytuj | edytuj kod]Zapis klasy do zewnętrznego pliku oraz odczyt z pliku można zrealizować przy użyciu modułu „pickle”.
import pickle
class Animal:
def __init__(self, attr="Horse"):
self.attr = attr
# serializacja – zapis do pliku
def test_serialize():
a = Animal()
print(a.attr)
with open("dump.dat", "wb") as f:
pickle.dump(a, f)
# deserializacja – odczyt z pliku
def test_deserialize():
with open("dump.dat", "rb") as f:
a = pickle.load(f)
print(a.attr)
def test():
test_serialize()
test_deserialize()
if __name__ == "__main__":
test()
Serializacja w języku C++
[edytuj | edytuj kod]Język C++ nie posiada wbudowanego wsparcia dla serializacji. Istnieją jednak przeznaczone do tego biblioteki, np. S11n.
Serializacja w mapowaniu obiektowo-relacyjnym
[edytuj | edytuj kod]Czasami w literaturze anglojęzycznej terminem serializacja (serialization) określa się zapis danych reprezentowanych przez dany obiekt do bazy danych[1].