1. Вступ
Нагадування: серіалізація — це спосіб зберегти стан об’єкта у вигляді рядка, файлу або потоку, щоб згодом можна було відновити цей об’єкт (десеріалізувати) — і у вас знову буде об’єкт з тими самими даними.
Типова життєва ситуація:
1) У вас є об’єкт, наприклад, користувач з ім’ям і віком.
2) Потрібно це зберегти — передати мережею, записати у файл або, скажімо, у формат JSON.
3) Потім дістаєте дані з рядка/файлу — і знову отримуєте об’єкт!
У C# це робиться буквально у кілька рядків. Погляньмо, як саме.
Наш застосунок
Ми розробляємо невеликий застосунок «Менеджер контактів». Нехай у нас є клас Person, який ми хочемо серіалізувати.
public class Person
{
// Публічні властивості обов’язкові для серіалізації!
public string Name { get; set; }
public int Age { get; set; }
// Конструктор без параметрів обов’язковий для XmlSerializer
public Person() { }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
Якщо ви не знайомі з таким синтаксисом, не переймайтеся — далі в курсі розберемо класи та властивості детально. Наразі сприймайте це як модель даних для експериментів. Зверніть увагу: публічні властивості Name і Age важливі для серіалізації, а для XmlSerializer потрібен публічний конструктор без параметрів.
2. Серіалізація у JSON за допомогою System.Text.Json
Найпростіший приклад
Як і обіцяли — показуємо! Ось як виглядає серіалізація у JSON:
using System;
using System.Text.Json;
Person person = new Person("Аліса", 28);
// Серіалізація: перетворюємо об’єкт на JSON-рядок
string json = JsonSerializer.Serialize(person);
Console.WriteLine("JSON:");
Console.WriteLine(json);
Що буде на екрані?
JSON:
{"Name":"Аліса","Age":28}
Усе просто: об’єкт → JSON. Ось вона — магія!
Десеріалізація — повертаємо усе назад
Тепер зробимо зворотну операцію — з JSON-рядка відновимо об’єкт:
string json = "{\"Name\":\"Боб\",\"Age\":35}";
// Десеріалізація: відновлюємо об’єкт типу Person з JSON-рядка
Person restored = JsonSerializer.Deserialize<Person>(json);
Console.WriteLine("Відновлений об’єкт:");
Console.WriteLine($"Імʼя: {restored.Name}, вік: {restored.Age}");
Результат:
Відновлений об’єкт:
Імʼя: Боб, вік: 35
Зверніть увагу: якщо структура даних не збігається (наприклад, немає такої властивості), відповідне поле залишиться зі значенням за замовчуванням (null для типів-посилань, 0 для чисел тощо).
3. Серіалізація/десеріалізація у XML за допомогою XmlSerializer
Серіалізація у XML — перший погляд
using System;
using System.IO;
using System.Xml.Serialization;
Person person = new Person("Катя", 22);
// Створюємо серіалізатор для типу Person
var serializer = new XmlSerializer(typeof(Person));
// Записуємо XML у файл
using (var stream = new FileStream("person.xml", FileMode.Create))
{
serializer.Serialize(stream, person);
// Потік автоматично закриється у кінці блоку using
}
Console.WriteLine("XML-файл створено!");
У файлі person.xml з’явиться щось на кшталт такого:
<?xml version="1.0"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Катя</Name>
<Age>22</Age>
</Person>
Важливий момент:
Для серіалізації у файл використовується потік (Stream, наприклад, FileStream). Якщо хочете серіалізувати у рядок (наприклад, для надсилання мережею), використовуйте StringWriter:
var serializer = new XmlSerializer(typeof(Person));
using (var sw = new StringWriter())
{
serializer.Serialize(sw, person);
string xmlString = sw.ToString();
Console.WriteLine(xmlString);
}
Десеріалізація з XML
using (var stream = new FileStream("person.xml", FileMode.Open))
{
// Не забудьте перетворити до потрібного типу!
Person restored = (Person)serializer.Deserialize(stream);
Console.WriteLine($"Імʼя: {restored.Name}, вік: {restored.Age}");
}
Ще раз: XmlSerializer вимагає, щоб у серіалізованого класу був публічний конструктор за замовчуванням (без параметрів).
4. Робота з файлами та рядками: що обрати?
У реальному житті серіалізація може виконуватися безпосередньо у рядок (для надсилання мережею/зберігання у БД) або у файл (для довготривалого зберігання).
Серіалізація у рядок (наприклад, для передачі через API):
- JSON: використовуємо JsonSerializer.Serialize(obj).
- XML: через StringWriter.
Серіалізація у файл (наприклад, для експорту/резервної копії):
- JSON: зберігаємо рядок у файл звичайними засобами запису файлів.
- XML: напряму пишемо у потік за допомогою XmlSerializer.
Приклад: серіалізація у файл і назад (JSON)
using System.IO;
using System.Text.Json;
Person person = new Person("Том", 42);
// Серіалізація у рядок
string json = JsonSerializer.Serialize(person);
// Запис у файл
File.WriteAllText("contact.json", json);
// Читання з файлу
string readJson = File.ReadAllText("contact.json");
Person restored = JsonSerializer.Deserialize<Person>(readJson);
Console.WriteLine($"{restored.Name}, вік: {restored.Age}");
5. Порівняння JSON vs XML
Візуалізація процесу
Щоб не плутатися, ось схема процесу серіалізації та десеріалізації:
flowchart LR
A[Об’єкт] -- серіалізація --> B[Рядок/Файл/Потік]
B -- десеріалізація --> C[Об’єкт]
A -. JSON або XML .-> B
B -. JSON або XML .-> C
Можете уявити, що серіалізація — це «спакувати валізу», а десеріалізація — «розпакувати» вдома.
Таблиця порівняння
| Функціональність | JSON (System.Text.Json) | XML (XmlSerializer) |
|---|---|---|
| Читання/запис у рядок | ++ | + |
| Читання/запис у файл | ++ | ++ |
| Потрібен конструктор без параметрів | Ні | Так |
| Зручність читання для людини | ++ | + |
| Сувора схема даних (валідація) | - | ++ |
| Гнучкість (атрибути налаштування) | + | ++ |
| Продуктивність | ++ | + |
| Сумісність з інтернетом | ++ | + |
++ — дуже добре, + — нормально, - — не підтримується
6. Як додати серіалізацію до нашого застосунку
Припустімо, у «Менеджері контактів» з’явилася можливість зберігати список контактів користувачів у файл і відновлювати його.
Контекст: у нас є список контактів (List<Person>).
List<Person> contacts = new List<Person>
{
new Person("Василь", 30),
new Person("Петро", 25),
new Person("Марія", 27)
};
// Зберігаємо всі контакти — серіалізуємо у JSON
string jsonContacts = JsonSerializer.Serialize(contacts);
File.WriteAllText("contacts.json", jsonContacts);
// Відновлюємо список контактів
string readContactsJson = File.ReadAllText("contacts.json");
List<Person> restored = JsonSerializer.Deserialize<List<Person>>(readContactsJson);
foreach (var c in restored)
{
Console.WriteLine($"{c.Name}, вік: {c.Age}");
}
Те саме можна зробити і з XML, але там є особливості. Для серіалізації колекцій через XmlSerializer зазвичай обгортають список в окремий клас-контейнер (кореневий елемент) — так простіше контролювати формат і назви елементів.
7. Типові помилки під час серіалізації та десеріалізації
Помилка № 1: невідповідність типів при серіалізації та десеріалізації.
Серіалізували List<Person> — десеріалізувати потрібно теж у List<Person>, а не у List<object> або IEnumerable<Person>. Інакше отримаєте помилку або неочікувані результати.
Помилка № 2: спроба серіалізувати непублічні поля та властивості.
Типові серіалізатори (JsonSerializer, XmlSerializer) працюють із публічними членами. Якщо у властивості немає публічного get або set, значення може не потрапити в JSON/XML, і ви шукатимете, чому там null.
Помилка № 3: відсутність конструктора без параметрів.
Для XmlSerializer публічний конструктор за замовчуванням обов’язковий. Якщо його немає — під час десеріалізації буде виняток.
Помилка № 4: хибне уявлення про посилання на об’єкти.
Якщо один об’єкт містить посилання на інший, серіалізація відбувається «за значенням» (як вкладений об’єкт). Самі посилання не зберігаються, що може призвести до дублювання даних або проблем із циклічними посиланнями.
Помилка № 5: спроба змішувати формати серіалізації.
XML і JSON — різні формати та серіалізатори. Не можна передати JSON-рядок у XmlSerializer і навпаки — буде помилка.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ