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) работают с публичными членами. Если у свойства нет публичного геттера/сеттера, значение может не попасть в JSON/XML, и вы будете искать, почему там null.
Ошибка №3: отсутствие конструктора без параметров.
Для XmlSerializer публичный конструктор по умолчанию обязателен. Если его нет — будет исключение при десериализации.
Ошибка №4: неправильное понимание ссылок на объекты.
Если один объект содержит ссылку на другой, сериализация идёт «по значению» (как вложенный объект). Ссылки как таковые не сохраняются, что может привести к дублированию данных или проблемам с циклическими ссылками.
Ошибка №5: попытка смешивать форматы сериализации.
XML и JSON — разные форматы и сериализаторы. Нельзя передать JSON-строку в XmlSerializer и наоборот — будет ошибка.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ