1. Wprowadzenie
Skoro postanowiliśmy „rozebrać LEGO-figurkę na części”, to rozróbmy, jakie mamy do tego narzędzia w .NET. Innymi słowy — jakie standardowe klasy pomagają serializować i deserializować obiekty do różnych formatów i co stoi za każdym z nich.
Jak zorganizowana jest rodzina serializacyjna .NET
Na bieżący (2025) moment .NET oferuje kilka podstawowych podejść do serializacji, każde z własnym zestawem klas i narzędzi. Główne i najczęściej spotykane:
- Serializacja JSON — podstawowa i najbardziej nowoczesna opcja, używana w większości projektów.
- Serializacja XML — trochę bardziej staroświecka, ale wciąż aktywnie stosowana.
- Serializacja binarna — tylko na specjalne przypadki i zwykle przez nowoczesne rozwiązania zewnętrzne.
Wiele przykładów i większość kodu w .NET 9 używa właśnie JSON, i to nie moda — to przemysłowy standard. Ale .NET wspiera też inne sposoby — na wszelki wypadek.
Główni bohaterowie serializacji .NET
| Format | Klasa serializacji | Prostota | Wydajność | Bezpieczeństwo | Aktualność |
|---|---|---|---|---|---|
| JSON | |
🔥🔥🔥 | 🔥🔥🔥 | 🔥🔥🔥 | Najbardziej aktualny |
| XML | |
🔥🔥 | 🔥🔥 | 🔥🔥 | Używany |
| JSON | Newtonsoft.Json (Json.NET) | 🔥🔥🔥 | 🔥🔥 | 🔥🔥🔥 | Bardzo popularny |
Krótko o każdym
- System.Text.Json: Nowy standard serializacji JSON w .NET, pojawił się w .NET Core 3.0, stał się domyślnym w .NET 5+. Szybki, lekki, bezpieczny, wbudowany „out of the box” w .NET 9. Dokumentacja
- XmlSerializer: Stara i sprawdzona opcja do serializacji do XML. Prosty w użyciu, ale z ograniczeniami (np. wymaga public-klasy i publicznych właściwości). Dobry do kompatybilności i ścisłych kontraktów danych. Dokumentacja
- Newtonsoft.Json: Przez długi czas był de-facto standardem serializacji JSON przed pojawieniem się System.Text.Json. Często używany w skomplikowanych scenariuszach (dynamika, prywatne właściwości itp.). Dokumentacja
Gdzie podział się BinaryFormatter?
Jeśli trafisz w sieci na porady używania BinaryFormatter — najpewniej to stary tutorial. Nie używaj BinaryFormatter: został usunięty z .NET 9 ze względów bezpieczeństwa. Współczesną serializację binarną zapewniają zewnętrzne rozwiązania — np. Protobuf lub MessagePack.
2. Proste przykłady
Spróbujmy zastosować w praktyce serializację i deserializację na przykładzie naszego znanego już klasy Player z uniwersum gry.
Przygotujmy klasę do serializacji
// Player.cs
public class Player
{
public string Name { get; set; }
public int Health { get; set; }
public bool IsAlive { get; set; }
public List<string> Inventory { get; set; }
public Position Position { get; set; }
}
public class Position
{
public int X { get; set; }
public int Y { get; set; }
}
a) Serializacja i deserializacja do JSON przy pomocy System.Text.Json
using System.Text.Json;
Player aragorn = new Player
{
Name = "Aragorn",
Health = 100,
IsAlive = true,
Inventory = new List<string> { "sword", "bow", "healing potion" },
Position = new Position { X = 10, Y = 25 }
};
// Serializacja obiektu Player do stringa JSON
string json = JsonSerializer.Serialize(aragorn);
// Wypiszmy JSON na ekran
Console.WriteLine(json);
// Deserializacja stringa JSON z powrotem do obiektu Player
Player? aragornClone = JsonSerializer.Deserialize<Player>(json);
// Sprawdźmy, czy klon działa :)
Console.WriteLine(aragornClone?.Name); // Powinno wypisać "Aragorn"
Tak to proste — żadnych „tańców z bębenkiem” i magicznych atrybutów. A teraz — jak to wygląda w XML.
b) Serializacja i deserializacja do XML przy pomocy XmlSerializer
using System.Xml.Serialization;
// Skonfigurujemy serializer dla klasy Player
XmlSerializer serializer = new XmlSerializer(typeof(Player));
// Serializacja do pliku
using FileStream fs = new FileStream("aragorn.xml", FileMode.Create);
serializer.Serialize(fs, aragorn); // Zapisujemy Aragorn do pliku XML
// Deserializacja z pliku
using FileStream fs = new FileStream("aragorn.xml", FileMode.Open);
Player aragornFromXml = (Player) serializer.Deserialize(fs)!;
Console.WriteLine(aragornFromXml.Name); // Powinno wypisać "Aragorn"
Uwaga! XmlSerializer wymaga, żeby klasy i ich właściwości były publiczne i miały domyślny konstruktor bez parametrów (jeśli nadpisujesz konstruktor — zrób go public i bez parametrów). W przeciwnym razie serializacja zakończy się błędem.
c) Serializacja i deserializacja do JSON przy pomocy Newtonsoft.Json
using Newtonsoft.Json; // Nie zapomnij dodać pakietu Newtonsoft.Json przez NuGet!
// Serializacja
string json2 = JsonConvert.SerializeObject(aragorn);
// Deserializacja
Player? aragornFromNewtonsoft = JsonConvert.DeserializeObject<Player>(json2);
Console.WriteLine(aragornFromNewtonsoft?.Name); // Znowu "Aragorn"
Wygląda prawie tak samo, ale Newtonsoft.Json ma wiele dodatkowych opcji — np. można serializować prywatne pola, konfigurować formatowanie i rozwiązywać nietrywialne scenariusze.
4. Przydatne niuanse
Standardowe serializatory i ich możliwości
| Klasa | Format | Wbudowana w .NET | Wymaga pakietu NuGet? | Przydatna do plików | Przydatna do API | Prostota |
|---|---|---|---|---|---|---|
|
JSON | Tak | Nie | Tak | Tak | Lekki |
|
JSON | Nie | Tak | Tak | Tak | Lekki |
|
XML | Tak | Nie | Tak | Często | Lekki |
Jak dowiedzieć się, której klasy użyć?
Jeśli nie wiesz, po co ci XML — prawie zawsze wybieraj JSON i System.Text.Json. To szybsze, prostsze i zgodne z nowoczesnymi praktykami.
XML warto wybierać, jeśli:
- Integrujesz się ze starymi systemami, które wymagają właśnie XML.
- Potrzebna jest ścisła schema i walidacja struktury danych.
- Struktury są duże, stabilne i ważna jest formalna kompatybilność (ustawienia, configi, wymiana z systemami enterprise).
JSON — jeśli:
- Tworzysz nowoczesną aplikację, komunikujesz się z webem i urządzeniami mobilnymi.
- Potrzebujesz prostoty, czytelności i kompaktowości.
- Nie chcesz ciągnąć dodatkowych zależności.
Newtonsoft.Json — jeśli:
- Potrzebujesz serializacji prywatnych pól, specjalnych customizacji, pełnej elastyczności.
- Albo projekt już dziedziczy tę bibliotekę i migracja teraz nie ma sensu.
5. Typowe błędy i pułapki
Kodowanie. Główne klasy serializacji (szczególnie przy pracy z plikami) używają domyślnie UTF-8. Jeśli widzisz „krzaczki”, sprawdź, jak czytasz/zapisujesz pliki i czy ustawiłeś jawnie kodowanie. Dokumentacja o konfiguracji kodowania.
Typy nieobsługiwane. Niektóre standardowe serializatory (zwłaszcza XML) nie potrafią serializować np. słowników (Dictionary), prywatnych/protected-pól, eventów, delegatów i interfejsów. Zwykle wspierane są publiczne proste właściwości i klasy.
Wersje klas. Jeśli zmieniasz strukturę klas (dodałeś/przemianowałeś/usunąłeś właściwości), stare zapisane dane mogą nie dać się odczytać albo zdeserializować niepoprawnie. Planuj wersjonowanie formatów.
Null-wartości. Przy deserializacji, jeśli w danych brakuje jakiegoś pola, odpowiadające mu właściwość dostanie wartość domyślną (dla typu referencyjnego — null). Nie zapomnij o walidacjach.
Atrybuty. Do precyzyjnej konfiguracji często używa się atrybutów typu [JsonIgnore], [XmlElement] itd. Pozwalają wykluczać właściwości, zmieniać nazwy elementów i kontrolować format — szczegóły w kolejnych wykładach.
GO TO FULL VERSION