CodeGym /Kursy /C# SELF /Podstawowe przykłady serializacji

Podstawowe przykłady serializacji

C# SELF
Poziom 45 , Lekcja 0
Dostępny

1. Wprowadzenie

Przypomnienie: Serializacja — to sposób zapisania stanu obiektu w postaci stringa, pliku lub strumienia, żeby później można było odtworzyć ten obiekt (deserializować) — i znów masz obiekt z tymi samymi danymi.

Typowa sytuacja życiowa:
1) Masz obiekt, np. użytkownika z imieniem i wiekiem.
2) Trzeba to zapisać — przesłać po sieci, zapisać do pliku lub np. do formatu JSON.
3) Potem dane z łańcucha/ pliku odczytujesz z powrotem — i znowu masz obiekt!

W C# da się to zrobić dosłownie w kilku linijkach. Zobaczmy, jak dokładnie.

Nasza aplikacja

Tworzymy małą aplikację "Menedżer kontaktów". Niech mamy klasę Person, którą chcemy serializować.

public class Person
{
    // Publiczne właściwości są wymagane do serializacji!
    public string Name { get; set; }
    public int Age { get; set; }

    // Konstruktor bez parametrów jest wymagany dla XmlSerializer
    public Person() { }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

Jeżeli nie znasz jeszcze takiego składnika, nie martw się — dalej w kursie omówimy klasy i właściwości szczegółowo. Na razie potraktuj to jako model danych do eksperymentów. Zwróć uwagę: publiczne właściwości Name i Age są istotne dla serializacji, a dla XmlSerializer potrzebny jest publiczny konstruktor bez parametrów.

2. Serializacja do JSON za pomocą System.Text.Json

Najprostszy przykład

Obiecaliśmy — pokazujemy! Tak wygląda serializacja do JSON:

using System;
using System.Text.Json;

Person person = new Person("Alisa", 28);

// Serializacja: zamieniamy obiekt na string JSON
string json = JsonSerializer.Serialize(person);

Console.WriteLine("JSON:");
Console.WriteLine(json);

Co pojawi się na ekranie?

JSON:
{"Name":"Alisa","Age":28}

Prosto: obiekt → JSON. Oto magia!

Deserializacja — odtwarzamy wszystko z powrotem

Teraz wykonamy operację odwrotną — z JSON-łańcucha odtworzymy obiekt:

string json = "{\"Name\":\"Bob\",\"Age\":35}";

// Deserializacja: odtwarzamy obiekt typu Person z łańcucha JSON
Person restored = JsonSerializer.Deserialize<Person>(json);

Console.WriteLine("Przywrócony obiekt:");
Console.WriteLine($"Imię: {restored.Name}, wiek: {restored.Age}");

Wynik:

Przywrócony obiekt:
Imię: Bob, wiek: 35

Zwróć uwagę: jeśli struktura danych się nie zgadza (np. brakuje jakiejś właściwości), odpowiednie pole pozostanie z wartością domyślną (null dla typów referencyjnych, 0 dla liczb itp.).

3. Serializacja/deserializacja do XML za pomocą XmlSerializer

Serializacja do XML — pierwszy rzut oka

using System;
using System.IO;
using System.Xml.Serialization;

Person person = new Person("Katya", 22);

// Tworzymy serializer dla typu Person
var serializer = new XmlSerializer(typeof(Person));

// Zapisujemy XML do pliku
using (var stream = new FileStream("person.xml", FileMode.Create))
{
    serializer.Serialize(stream, person);
    // Strumień zostanie automatycznie zamknięty na końcu using-bloku
}
Console.WriteLine("Plik XML utworzony!");

W pliku person.xml pojawi się coś w tym stylu:

<?xml version="1.0"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>Katya</Name>
  <Age>22</Age>
</Person>

Ważna uwaga:
Do serializacji do pliku używamy strumienia (Stream, np. FileStream). Jeśli chcesz serializować do łańcucha (np. do wysłania po sieci), użyj StringWriter:

var serializer = new XmlSerializer(typeof(Person));
using (var sw = new StringWriter())
{
    serializer.Serialize(sw, person);
    string xmlString = sw.ToString();
    Console.WriteLine(xmlString);
}

Deserializacja z XML

using (var stream = new FileStream("person.xml", FileMode.Open))
{
    // Nie zapomnij rzutować do odpowiedniego typu!
    Person restored = (Person)serializer.Deserialize(stream);
    Console.WriteLine($"Imię: {restored.Name}, wiek: {restored.Age}");
}

Jeszcze raz: XmlSerializer wymaga, żeby serializowana klasa miała publiczny konstruktor domyślny (bez parametrów).

4. Praca z plikami i łańcuchami: co wybrać?

W praktyce serializacja może iść bezpośrednio do łańcucha (do przesyłu po sieci/przechowywania w DB) lub do pliku (do długotrwałego przechowywania).

Serializacja do łańcucha (np. do przesłania przez API):

  • JSON: używamy JsonSerializer.Serialize(obj).
  • XML: przez StringWriter.

Serializacja do pliku (np. do eksportu/kopii zapasowej):

  • JSON: zapisujemy string do pliku standardowymi metodami zapisu.
  • XML: bezpośrednio piszemy do strumienia przy pomocy XmlSerializer.

Przykład: serializacja do pliku i z powrotem (JSON)

using System.IO;
using System.Text.Json;

Person person = new Person("Tom", 42);

// Serializacja do stringa
string json = JsonSerializer.Serialize(person);

// Zapis do pliku
File.WriteAllText("contact.json", json);

// Odczyt z pliku
string readJson = File.ReadAllText("contact.json");
Person restored = JsonSerializer.Deserialize<Person>(readJson);

Console.WriteLine($"{restored.Name}, wiek: {restored.Age}");

5. Porównanie JSON vs XML

Wizualizacja procesu

Aby się nie pogubić, oto schemat procesu serializacji i deserializacji:

flowchart LR
    A[Obiekt] -- serializacja --> B[String/Plik/Strumień]
    B -- deserializacja --> C[Obiekt]

    A -. JSON lub XML .-> B
    B -. JSON lub XML .-> C

Możesz sobie wyobrazić, że serializacja to "spakować walizkę", a deserializacja to "rozpakować" w domu.

Tabela porównawcza

Funkcjonalność JSON (System.Text.Json) XML (XmlSerializer)
Odczyt/zapis do stringa ++ +
Odczyt/zapis do pliku ++ ++
Wymaga konstruktora bez parametrów Nie Tak
Czytelność dla człowieka ++ +
Ścisły schemat danych (walidacja) - ++
Elastyczność (atrybuty do customizacji) + ++
Wydajność ++ +
Kompatybilność z internetem ++ +

++ — bardzo dobrze, + — w porządku, - — nieobsługiwane

6. Jak dodać serializację do naszej aplikacji

Niech w "Menedżerze kontaktów" pojawi się możliwość zapisywania listy kontaktów do pliku i jej przywracania.

Kontekst: mamy listę kontaktów (List<Person>).

List<Person> contacts = new List<Person>
{
    new Person("Vasya", 30),
    new Person("Petya", 25),
    new Person("Masha", 27)
};

// Zapisujemy wszystkie kontakty — serializujemy do JSON
string jsonContacts = JsonSerializer.Serialize(contacts);
File.WriteAllText("contacts.json", jsonContacts);

// Przywracamy listę kontaktów
string readContactsJson = File.ReadAllText("contacts.json");
List<Person> restored = JsonSerializer.Deserialize<List<Person>>(readContactsJson);

foreach (var c in restored)
{
    Console.WriteLine($"{c.Name}, wiek: {c.Age}");
}

To samo da się zrobić z XML, ale tam są różnice. Przy serializacji kolekcji przez XmlSerializer zwykle opakowuje się listę w osobną klasę-kontener (element root) — wtedy łatwiej kontrolować format i nazwy elementów.

7. Typowe błędy przy serializacji i deserializacji

Błąd nr 1: niezgodność typów przy serializacji i deserializacji.
Zserializowałeś List<Person> — deserializować musisz też do List<Person>, a nie do List<object> czy IEnumerable<Person>. W przeciwnym razie dostaniesz błąd lub nieoczekiwane rezultaty.

Błąd nr 2: próba serializacji niepublicznych pól i właściwości.
Typowe serializery (JsonSerializer, XmlSerializer) pracują na publicznych elementach. Jeśli właściwość nie ma publicznego gettera/settera, wartość może nie trafić do JSON/XML, i będziesz się zastanawiać, dlaczego tam jest null.

Błąd nr 3: brak konstruktora bez parametrów.
Dla XmlSerializer publiczny konstruktor domyślny jest obowiązkowy. Jeśli go nie ma — będzie wyjątek przy deserializacji.

Błąd nr 4: błędne rozumienie referencji do obiektów.
Jeśli jeden obiekt zawiera referencję do innego, serializacja odbywa się "przez wartość" (jako obiekt zagnieżdżony). Referencje same w sobie nie są zapisywane, co może prowadzić do duplikacji danych lub problemów z cyklicznymi referencjami.

Błąd nr 5: próba mieszania formatów serializacji.
XML i JSON to różne formaty i różni serializerzy. Nie możesz podać JSON-łańcucha do XmlSerializer i odwrotnie — dostaniesz błąd.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION