Serializacja – Wikipedia, wolna encyklopedia
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].