JavaRush /Курсы /C# SELF /Чтение и запись XML: XmlSe...

Чтение и запись XML: XmlSerializer

C# SELF
48 уровень , 0 лекция
Открыта

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 — задавайте кодировку явно.

2
Задача
C# SELF, 48 уровень, 0 лекция
Недоступна
Сериализация списка объектов
Сериализация списка объектов
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ