1. Введение
XML (eXtensible Markup Language) — расширяемый язык разметки, популярный формат для хранения и обмена сложными структурированными данными. В отличие от JSON, XML не отличается лаконичностью, но до сих пор активно используется в банковской сфере, обмене между государственными сервисами, документах и интеграциях с 1С, SAP, Oracle и т. д. Некоторые данные в Windows и .NET (например, конфиги) по-прежнему хранятся именно в формате XML.
Визуально XML напоминает HTML, только без фиксированного набора тегов вроде <body> и <div> — вы сами определяете структуру своих элементов. Пример:
<Person>
<FirstName>Ivan</FirstName>
<LastName>Petrov</LastName>
<Age>30</Age>
</Person>
Основы XML и соответствие объектам C#
В отличие от JSON, в XML нет специальных типов для массивов или булевых значений — все представлено элементами и атрибутами. Структуры-списки реализуются как повторяющиеся элементы с одинаковым именем.
Чтобы удобно работать с XML в C#, используется механизм сериализации/десериализации через XmlSerializer.
Сериализация — преобразование C#-объекта в XML-строку. Десериализация — обратный процесс: получение C#-объекта из XML.
Это работает “из коробки”, если ваши классы соответствуют ряду требований — ниже разберём.
Зачем использовать XmlSerializer?
XmlSerializer — стандартный инструмент .NET (пространство имён System.Xml.Serialization) для преобразования объектов в XML и обратно. Он прост, типобезопасен и подходит для импорта/экспорта данных, конфигов, обмена между системами и интеграций с гос. сервисами.
2. Пример: сериализация объекта
Пусть у нас есть модель пользователя:
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
Сериализуем объект в XML:
using System.Xml.Serialization;
User user = new User { FirstName = "Иван", LastName = "Петров", Age = 30 };
// Для сериализации нужен тип
XmlSerializer serializer = new XmlSerializer(typeof(User));
// Записываем XML в файл
using FileStream fs = new FileStream("user.xml", FileMode.Create);
serializer.Serialize(fs, user);
Что происходит?
- Создаём объект класса User.
- Создаём экземпляр XmlSerializer для типа User.
- Открываем файл через FileStream.
- Вызываем метод Serialize(), который превращает объект в XML и записывает его в файл.
Содержимое файла user.xml:
<?xml version="1.0"?>
<User xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FirstName>Иван</FirstName>
<LastName>Петров</LastName>
<Age>30</Age>
</User>
Вот так просто — минимум кода и готовый XML.
3. Десериализация: читаем XML обратно в объект
Читаем созданный XML и восстанавливаем объект C#:
// Открываем XML-файл
using FileStream fs = new FileStream("user.xml", FileMode.Open);
User? loadedUser = serializer.Deserialize(fs) as User;
Console.WriteLine($"{loadedUser?.FirstName} {loadedUser?.LastName}, возраст: {loadedUser?.Age}");
Метод Deserialize() возвращает object, поэтому делаем приведение к User (через as). Если файл корректный и соответствует модели — объект восстановится полностью.
4. Как работает сериализация: типичные особенности
Сериализация в XML требует соблюдения правил:
• Класс для сериализации должен иметь публичный конструктор по умолчанию (public без параметров).
• Сериализуемые свойства — публичные: нужны и геттер, и сеттер. Приватные члены игнорируются.
• Не работают свойства только для чтения, а также статические и абстрактные свойства.
При нарушении правил получите InvalidOperationException с сообщением вроде "XmlSerializer cannot serialize this type".
5. Сериализация коллекций: массивы и списки
Если нужно сериализовать несколько пользователей — используем оборачивающий класс:
public class UserList
{
public List<User> Users { get; set; } = new List<User>();
}
Создадим коллекцию и сериализуем:
UserList users = new UserList
{
Users = new List<User>
{
new User { FirstName = "Иван", LastName = "Петров", Age = 30 },
new User { FirstName = "Мария", LastName = "Иванова", Age = 25 }
}
};
XmlSerializer serializer = new XmlSerializer(typeof(UserList));
using FileStream fs = new FileStream("users.xml", FileMode.Create);
serializer.Serialize(fs, users);
Получим XML:
<?xml version="1.0"?>
<UserList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Users>
<User>
<FirstName>Иван</FirstName>
<LastName>Петров</LastName>
<Age>30</Age>
</User>
<User>
<FirstName>Мария</FirstName>
<LastName>Иванова</LastName>
<Age>25</Age>
</User>
</Users>
</UserList>
Можно ли сериализовать просто List<User>? Можно, но корневой элемент будет называться ArrayOfUser, что не всегда подходит интеграциям.
6. Атрибуты для управления сериализацией
Через атрибуты можно управлять форматом XML: переименовывать элементы, сериализовать как атрибуты, игнорировать поля и т. д. Полезные атрибуты: [XmlElement], [XmlAttribute], [XmlArray], [XmlIgnore].
Сериализация как атрибуты
Запишем имя и фамилию как атрибуты, а возраст — элементом:
public class User
{
[XmlAttribute]
public string FirstName { get; set; }
[XmlAttribute]
public string LastName { get; set; }
[XmlElement]
public int Age { get; set; }
}
Результат:
<User FirstName="Иван" LastName="Петров">
<Age>30</Age>
</User>
Управление именами элементов
Переименуем теги в человекочитаемые:
public class User
{
[XmlElement("Имя")]
public string FirstName { get; set; }
[XmlElement("Фамилия")]
public string LastName { get; set; }
[XmlElement("Возраст")]
public int Age { get; set; }
}
Результирующий XML:
<User>
<Имя>Иван</Имя>
<Фамилия>Петров</Фамилия>
<Возраст>30</Возраст>
</User>
Игнорирование свойств
Если свойство сериализовать не нужно — пометьте его [XmlIgnore]:
public class User
{
public string FirstName { get; set; }
[XmlIgnore]
public string InternalCode { get; set; }
}
7. Сериализация иерархии и вложенных объектов
Вложенные классы легко превращаются в вложенные XML-элементы:
public class Address
{
public string City { get; set; }
public string Street { get; set; }
}
public class User
{
public string FirstName { get; set; }
public Address Address { get; set; }
}
var user = new User
{
FirstName = "Анна",
Address = new Address { City = "Неонград", Street = "Вязовая" }
};
XmlSerializer serializer = new XmlSerializer(typeof(User));
using var fs = new FileStream("user_with_address.xml", FileMode.Create);
serializer.Serialize(fs, user);
XML:
<User>
<FirstName>Анна</FirstName>
<Address>
<City>Неонград</City>
<Street>Вязовая</Street>
</Address>
</User>
8. Сериализация в/из строки (без файлов)
Сериализация в строку
using System.Text;
var serializer = new XmlSerializer(typeof(User));
using var stringWriter = new StringWriter();
serializer.Serialize(stringWriter, user);
string xml = stringWriter.ToString();
Console.WriteLine(xml);
Десериализация из строки
var xml = "<User><FirstName>Анна</FirstName></User>";
using var stringReader = new StringReader(xml);
User user = (User)serializer.Deserialize(stringReader);
Console.WriteLine(user.FirstName);
9. Пример: экспорт и импорт данных приложения
Экспорт/импорт списка пользователей в файл XML:
public static void ExportUsers(UserList users, string path)
{
var serializer = new XmlSerializer(typeof(UserList));
using var fs = new FileStream(path, FileMode.Create);
serializer.Serialize(fs, users);
}
public static UserList ImportUsers(string path)
{
var serializer = new XmlSerializer(typeof(UserList));
using var fs = new FileStream(path, FileMode.Open);
return (UserList)serializer.Deserialize(fs);
}
Применение: резервное копирование, обмен с другими сервисами, интеграции. На собеседованиях вопросы про XmlSerializer встречаются чаще, чем кажется — особенно в компаниях, работающих с госуслугами и банками.
10. Типичные ошибки и особенности работы
Если поле — не public, оно не сериализуется.
Свойства только для чтения игнорируются.
Свойства с типом интерфейса или абстрактного класса сериализатор не поддерживает.
Тип DateTime сериализуется в формате XSD (см. документацию).
Коллекции вида массивов и List<T> работают предсказуемо, а с ObservableCollection<T> возможны сюрпризы у сторонних систем.
Нюансы кодировок: при выводе через StreamWriter без явной кодировки результат может оказаться в UTF-16; для совместимости чаще ожидают UTF-8 — задавайте кодировку явно.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ