Last modified: June 06, 2026

This article is written in: 🇵🇱

Serializacja

Serializacja to proces przekształcania obiektów lub struktur danych w format, który można łatwo przechowywać, przesyłać i odtwarzać. Dzięki serializacji możemy zapisać stan obiektu w pliku, przesłać go przez sieć lub przechowywać w bazie danych, a następnie w dowolnym momencie przywrócić go do pierwotnej postaci poprzez deserializację. Jest to kluczowy mechanizm w programowaniu, który umożliwia efektywną wymianę danych między różnymi systemami i komponentami.

+--------------------+       Serializacja        +-----------------+
|    Obiekt Pythona  | ------------------------> |     Bajty        |
+--------------------+                           +-----------------+
        ^                                                  |
        |                                                  |
        |                 Deserializacja                   |
        |                                                  |
        |                                                  v
+--------------------+       Deserializacja       +-----------------+
|       Bajty        | <------------------------  |   Obiekt Pythona |
+--------------------+                           +-----------------+

Zastosowania serializacji

Serializacja znajduje zastosowanie w wielu obszarach programowania:

Serializacja z użyciem modułu pickle

W Pythonie jednym z podstawowych narzędzi do serializacji jest moduł pickle. Pozwala on na konwersję niemal dowolnych obiektów Pythona do ciągu bajtów i odwrotnie. Poniżej przedstawiono przykład, jak można zserializować i deserializować obiekt klasy Czlowiek:

import pickle

class Czlowiek:
    def __init__(self, imie, numer):
        self.imie = imie
        self.numer = numer

    def __repr__(self):
        return f'Imię: {self.imie}, Numer: {self.numer}'

# Ścieżka do pliku, w którym zapiszemy dane
sciezka = 'przyklad.pickle'

# Serializacja obiektu
with open(sciezka, 'wb') as plik:
    obiekt = Czlowiek('James', 10)
    pickle.dump(obiekt, plik)

# Deserializacja obiektu
with open(sciezka, 'rb') as plik:
    obiekt = pickle.load(plik)
    print(obiekt)

Po uruchomieniu tego kodu, otrzymamy wynik:

Imię: James, Numer: 10

Ostrzeżenie dotyczące bezpieczeństwa

Korzystając z modułu pickle, należy zachować ostrożność. Deserializacja danych z niezaufanych źródeł może być niebezpieczna, ponieważ może prowadzić do wykonania złośliwego kodu. Dlatego zaleca się używanie pickle tylko z danymi pochodzącymi z zaufanych źródeł. W sytuacjach wymagających większego bezpieczeństwa warto rozważyć użycie innych formatów, takich jak JSON czy XML.

Serializacja z użyciem modułu json

Format JSON (JavaScript Object Notation) jest jednym z najpopularniejszych formatów do wymiany danych między aplikacjami, zwłaszcza w kontekście aplikacji webowych. Python oferuje moduł json, który pozwala na serializację i deserializację obiektów do formatu JSON.

import json

class Czlowiek:
    def __init__(self, imie, numer):
        self.imie = imie
        self.numer = numer

    def to_dict(self):
        return {'imie': self.imie, 'numer': self.numer}

    @classmethod
    def from_dict(cls, dane):
        return cls(dane['imie'], dane['numer'])

# Serializacja obiektu do JSON
obiekt = Czlowiek('James', 10)
json_str = json.dumps(obiekt.to_dict())
print(json_str)

Wynik:

{"imie": "James", "numer": 10}

Aby zdeserializować dane z formatu JSON:

# Deserializacja obiektu z JSON
dane = json.loads(json_str)
obiekt = Czlowiek.from_dict(dane)
print(obiekt)

Wynik:

Imię: James, Numer: 10

Serializacja z użyciem modułu xml.etree.ElementTree

Format XML (eXtensible Markup Language) jest kolejnym popularnym formatem do przechowywania i wymiany danych. W Pythonie możemy użyć modułu xml.etree.ElementTree do pracy z danymi XML.

import xml.etree.ElementTree as ET

class Czlowiek:
    def __init__(self, imie, numer):
        self.imie = imie
        self.numer = numer

    def to_xml(self):
        czlowiek = ET.Element('Czlowiek')
        imie_elem = ET.SubElement(czlowiek, 'Imie')
        imie_elem.text = self.imie
        numer_elem = ET.SubElement(czlowiek, 'Numer')
        numer_elem.text = str(self.numer)
        return ET.tostring(czlowiek, encoding='unicode')

    @classmethod
    def from_xml(cls, xml_data):
        root = ET.fromstring(xml_data)
        imie = root.find('Imie').text
        numer = int(root.find('Numer').text)
        return cls(imie, numer)

# Serializacja obiektu do XML
obiekt = Czlowiek('James', 10)
xml_str = obiekt.to_xml()
print(xml_str)

Wynik:

<Czlowiek><Imie>James</Imie><Numer>10</Numer></Czlowiek>

Deserializacja z XML:

# Deserializacja obiektu z XML
obiekt = Czlowiek.from_xml(xml_str)
print(obiekt)

Wynik:

Imię: James, Numer: 10

Diagram ilustrujący proces serializacji

Aby lepiej zrozumieć proces serializacji i deserializacji, spójrzmy na poniższy diagram:

|     Obiekt Pythona    |
+-----------+-----------+
            |
            |  Serializacja
            v
+-----------------------+
|     Format danych     |
|   (JSON, XML, bajty)  |
+-----------+-----------+
            |
            |  Deserializacja
            v
+-----------------------+
|     Obiekt Pythona    |
+-----------------------+

Proces ten polega na przekształceniu obiektu Pythona w format danych, który można łatwo przechowywać lub przesyłać, a następnie przywróceniu go do pierwotnej postaci.

Praktyczne zastosowanie serializacji

Wyobraźmy sobie aplikację, w której użytkownik tworzy złożone obiekty, takie jak profile użytkowników, konfiguracje czy stany gry. Chcemy umożliwić zapisanie tych obiektów do pliku, aby można było je później wczytać lub przesłać innym użytkownikom.

Zapis stanu gry do pliku JSON

import json

class Postac:
    def __init__(self, imie, poziom, ekwipunek):
        self.imie = imie
        self.poziom = poziom
        self.ekwipunek = ekwipunek

    def to_dict(self):
        return {
            'imie': self.imie,
            'poziom': self.poziom,
            'ekwipunek': self.ekwipunek
        }

    @classmethod
    def from_dict(cls, dane):
        return cls(dane['imie'], dane['poziom'], dane['ekwipunek'])

# Tworzenie obiektu postaci
postac = Postac('Aragorn', 20, ['Miecz', 'Tarcza', 'Pierścień'])

# Serializacja do JSON i zapis do pliku
with open('postac.json', 'w') as plik:
    json.dump(postac.to_dict(), plik)

# Deserializacja z pliku JSON
with open('postac.json', 'r') as plik:
    dane = json.load(plik)
    postac_wczytana = Postac.from_dict(dane)

print(f"Imię: {postac_wczytana.imie}, Poziom: {postac_wczytana.poziom}, Ekwipunek: {postac_wczytana.ekwipunek}")

Wynik:

Imię: Aragorn, Poziom: 20, Ekwipunek: ['Miecz', 'Tarcza', 'Pierścień']

Przesyłanie danych między aplikacjami

Jeśli chcemy przesłać dane między różnymi aplikacjami lub usługami sieciowymi, możemy użyć formatu JSON lub XML, który jest uniwersalny i obsługiwany przez wiele języków programowania.

Uwagi dotyczące bezpieczeństwa

Podczas korzystania z serializacji, szczególnie z modułem pickle, należy być świadomym potencjalnych zagrożeń. Deserializacja danych z nieznanych źródeł może prowadzić do wykonania złośliwego kodu. Zawsze upewnij się, że dane pochodzą z zaufanego źródła lub używaj bezpieczniejszych metod serializacji, takich jak JSON, które nie wykonują kodu podczas deserializacji.

Porównanie formatów serializacji

Format Moduł Czytelność Szybkość Bezpieczeństwo Interoperacyjność Obsługuje obiekty Pythona
pickle pickle Nie Bardzo szybki Niskie Tylko Python Tak (prawie wszystkie)
JSON json Tak Szybki Wysokie Uniwersalny Nie (wymaga konwersji)
XML xml.etree Tak Wolny Średnie Uniwersalny Nie (wymaga konwersji)
YAML pyyaml Tak Średni Średnie* Uniwersalny Częściowo
MessagePack msgpack Nie Bardzo szybki Wysokie Uniwersalny Nie
Protocol Buffers protobuf Nie Najszybszy Wysokie Uniwersalny Nie (wymaga schematu)

* YAML z yaml.safe_load() jest bezpieczny; yaml.load() bez Loadera — nie.

Kiedy używać którego formatu?

Scenariusz Rekomendowany format
API webowe (REST) JSON
Konfiguracja czytelna dla człowieka YAML lub JSON
Wymiana danych między mikroserwisami MessagePack / Protobuf
Zapis stanu aplikacji Python (zaufane źródło) pickle
Wymiana z systemami enterprise (SOAP, RSS) XML
Trwałe przechowywanie z wersjowaniem JSON + schemat
Maksymalna wydajność i minimalny rozmiar Protocol Buffers

Serializacja z użyciem YAML

YAML (YAML Ain't Markup Language) jest popularnym formatem konfiguracyjnym dzięki swojej czytelności:

# pip install pyyaml
import yaml

dane = {
    'serwer': {
        'host': 'localhost',
        'port': 8080,
        'debug': True
    },
    'baza_danych': {
        'url': 'sqlite:///app.db',
        'pool_size': 5
    }
}

# Serializacja do YAML
yaml_str = yaml.dump(dane, default_flow_style=False, allow_unicode=True)
print(yaml_str)

# Deserializacja z YAML (ZAWSZE używaj safe_load!)
odczytane = yaml.safe_load(yaml_str)
print(odczytane['serwer']['port'])  # 8080

Zaawansowane techniki z JSON

Obsługa niestandardowych typów z JSONEncoder

import json
from datetime import datetime, date
from decimal import Decimal

class RozszerzonyEncoder(json.JSONEncoder):
    """Encoder obsługujący datetime, date i Decimal."""
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        if isinstance(obj, date):
            return obj.isoformat()
        if isinstance(obj, Decimal):
            return float(obj)
        return super().default(obj)

dane = {
    'utworzono': datetime.now(),
    'data': date.today(),
    'cena': Decimal('19.99')
}

json_str = json.dumps(dane, cls=RozszerzonyEncoder, indent=2, ensure_ascii=False)
print(json_str)

Walidacja struktury z jsonschema

# pip install jsonschema
from jsonschema import validate, ValidationError

schemat = {
    "type": "object",
    "properties": {
        "imie": {"type": "string", "minLength": 1},
        "wiek": {"type": "integer", "minimum": 0},
        "email": {"type": "string", "format": "email"}
    },
    "required": ["imie", "wiek"]
}

# Poprawne dane
validate({"imie": "Anna", "wiek": 30}, schemat)

# Niepoprawne dane — rzuci ValidationError
try:
    validate({"imie": "", "wiek": -5}, schemat)
except ValidationError as e:
    print(f"Błąd walidacji: {e.message}")

Wydajność — porównanie rozmiarów i szybkości

import json
import pickle
import sys
import time

dane = [{"id": i, "wartość": i * 3.14, "nazwa": f"element_{i}"} for i in range(10000)]

# Rozmiar serializowanych danych
json_bytes = json.dumps(dane).encode()
pickle_bytes = pickle.dumps(dane)

print(f"JSON:   {len(json_bytes):>10,} bajtów")
print(f"Pickle: {len(pickle_bytes):>10,} bajtów")

# Szybkość serializacji
start = time.perf_counter()
for _ in range(100):
    json.dumps(dane)
t_json = time.perf_counter() - start

start = time.perf_counter()
for _ in range(100):
    pickle.dumps(dane)
t_pickle = time.perf_counter() - start

print(f"JSON:   {t_json:.3f}s")
print(f"Pickle: {t_pickle:.3f}s")