Serializacja

Z Wikipedii, wolnej encyklopedii
(Przekierowano z Deserializacja)

Serializacja – w programowaniu proces przekształcania obiektów, tj. instancji określonych klas, do postaci szeregowej, czyli w strumień bajtów, 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:

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].

Zobacz też[edytuj | edytuj kod]

Przypisy[edytuj | edytuj kod]