JavaRush /Курси /C# SELF /Налаштування XML-серіалізації

Налаштування XML-серіалізації

C# SELF
Рівень 48, Лекція 1
Відкрита

1. Вступ

Ви вже знаєте, що .NET уміє серіалізувати й десеріалізувати об’єкти в XML за замовчуванням, спираючись на публічні поля та властивості. Та коли потрібно отримати акуратний XML, сумісний із зовнішніми програмами й стандартами, або коли не слід додавати до XML певні внутрішні деталі, знадобиться тонке налаштування.

У C# для цього призначені атрибути серіалізації — спеціальні «мітки», якими позначають класи, властивості або поля. Завдяки їм можна керувати практично всім: які поля потраплять до XML, які ігнорувати, як саме називатиметься елемент, чи перетворювати його на атрибут, чи надавати йому складну структуру, а також визначати порядок елементів.

Уявімо серіалізацію за замовчуванням як автозаповнення анкети: імʼя, прізвище, по батькові — у свої клітинки, решта — як вийде. А з атрибутами ви самі вирішуєте, який рядок куди записати, що пропустити, що виділити, а що взагалі сховати.

Відповідність об’єкта та XML

Клас/Властивість Атрибут Результат у XML
[XmlRoot("Persons")]
Клас UserList
<Persons>...</Persons>
List<User> Users
[XmlElement("Person")]
<Person>...</Person>
int Id
[XmlAttribute("id")]
id="..." (атрибут)
string Name
[XmlElement("FullName")]
<FullName>...</FullName>
string[] Emails
[XmlArray("EMails")][XmlArrayItem("Email")]
<EMails><Email>...</Email></EMails>
DateTime RegisteredAt
[XmlIgnore] (див. RegisteredAtString) -
string RegisteredAtString
[XmlAttribute("registered")]
registered="..."
Address[] Addresses
[XmlArray("UserAddresses")][XmlArrayItem("Address")]
<UserAddresses>...</UserAddresses>

2. Огляд основних атрибутів XML-серіалізації

Пройдімося найпоширенішими атрибутами, які .NET‑розробники використовують уже багато років!

Атрибут [XmlElement] — ім’я елемента

Змінює ім’я елемента в XML або позначає властивість/поле як XML‑елемент.

public class Person
{
    [XmlElement("FullName")]
    public string Name { get; set; }
}

XML:

<Person>
  <FullName>Василь Петров</FullName>
</Person>

Якщо атрибут не вказати — за замовчуванням ім’я елемента збігатиметься з ім’ям властивості/поля.

Атрибут [XmlAttribute] — серіалізація у XML-атрибут

Дозволяє серіалізувати властивість/поле як атрибут XML, а не як елемент.

public class Person
{
    [XmlAttribute("id")]
    public int Id { get; set; }
}

XML:

<Person id="123"></Person>

У реальних XML‑API атрибути часто використовують для ідентифікаторів, дат, прапорців тощо.

Атрибут [XmlIgnore] — пропустити поле/властивість

Цей атрибут повністю виключає властивість із процесу серіалізації та десеріалізації: позначене ним поле або властивість просто не потрапить до фінального XML.

public class Person
{
    [XmlIgnore]
    public string InternalNote { get; set; }
}

XML: (властивості InternalNote тут немає і не буде)

Атрибути [XmlArray] і [XmlArrayItem] — контроль над колекціями

Спеціальні атрибути для масивів і колекцій. [XmlArray] задає ім’я зовнішньої обгортки масиву, а [XmlArrayItem] — ім’я окремого елемента.

public class Person
{
    [XmlArray("Phones")]
    [XmlArrayItem("Phone")]
    public string[] PhoneNumbers { get; set; }
}

XML:

<Person>
  <Phones>
    <Phone>+12951234567</Phone>
    <Phone>+12876543210</Phone>
  </Phones>
</Person>

Атрибут [XmlRoot] — назва кореневого елемента

Позначає сам клас, щоб змінити ім’я кореневого елемента в XML.

[XmlRoot("User")]
public class Person
{
    public string Name { get; set; }
}

XML:

<User>
  <Name>Анна</Name>
</User>

Атрибут [XmlText] — серіалізація у текстовий вміст елемента

Іноді потрібно, щоб значення властивості стало просто текстом усередині елемента, а не його вкладеним піделементом або атрибутом.

public class Note
{
    [XmlText]
    public string Content { get; set; }
}

XML:

<Note>Зателефонувати бабусі!</Note>

Атрибути [XmlNamespaceDeclarations], [XmlElement(Type = ...)]

Для складніших сценаріїв (наприклад, робота з просторами імен, підтримка наслідування тощо) існують і специфічніші атрибути. Радимо переглянути офіційну документацію Microsoft для повного переліку.

3. Практика: удосконалюємо серіалізацію на реальному прикладі

Пройдімо крок за кроком і удосконалимо наш навчальний застосунок, довівши XML‑серіалізацію до високого рівня — щоб вона відповідала навіть найприскіпливішим вимогам.

Базовий клас без атрибутів

Припустімо, у нас є такий клас користувача:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string[] Emails { get; set; }
    public DateTime RegisteredAt { get; set; }
    public string Password { get; set; }
}

Серіалізація за замовчуванням сформує приблизно такий XML (і ще й дату збереже у власному форматі):

<User>
  <Id>42</Id>
  <Name>Ivan Ivanov</Name>
  <Emails>
    <string>user@mail.com</string>
    <string>admin@site.org</string>
  </Emails>
  <RegisteredAt>2024-07-01T00:00:00</RegisteredAt>
  <Password>qwerty</Password>
</User>

Що не так:

  • Пароль краще взагалі не серіалізувати.
  • Хочемо мати дату як атрибут і в зрозумілому форматі.
  • Хочемо список електронних адрес у зручному вигляді.
  • Хочемо кореневий елемент <Person>.

Додаємо налаштування через атрибути

[XmlRoot("Person")]
public class User
{
    [XmlAttribute("id")]
    public int Id { get; set; }

    [XmlElement("FullName")]
    public string Name { get; set; }

    [XmlArray("EMails")]
    [XmlArrayItem("Email")]
    public string[] Emails { get; set; }

    [XmlAttribute("registered")]
    public string RegisteredAtString
    {
        get => RegisteredAt.ToString("yyyy-MM-dd");
        set => RegisteredAt = DateTime.Parse(value);
    }

    [XmlIgnore]
    public string Password { get; set; }

    [XmlIgnore]
    public DateTime RegisteredAt { get; set; }
}

Пояснення тонкощів:

  • Використовуємо [XmlAttribute("registered")] для серіалізації дати як атрибута.
  • Щоб серіалізувати не сам DateTime, а його рядкове представлення, додаємо окрему властивість RegisteredAtString, а реальне поле приховуємо через [XmlIgnore].
  • Пароль приховуємо за допомогою [XmlIgnore].
  • Масив електронних адрес налаштовано так, що зовнішній тег — <EMails>, а кожна адреса — <Email>.

Результат:

<Person id="42" registered="2024-07-01">
  <FullName>Ivan Ivanov</FullName>
  <EMails>
    <Email>user@mail.com</Email>
    <Email>admin@site.org</Email>
  </EMails>
</Person>

4. Робота з вкладеними об’єктами

XML особливо зручний, коли є вкладені об’єкти (наприклад, користувач і його адреси).

public class Address
{
    [XmlAttribute]
    public string City { get; set; }

    [XmlText]
    public string Details { get; set; }
}

public class User
{
    // ...інші властивості, див. вище...

    public Address[] Addresses { get; set; }
}

Додамо налаштування для масиву адрес:

[XmlArray("UserAddresses")]
[XmlArrayItem("Address")]
public Address[] Addresses { get; set; }

Тоді XML виглядатиме так:

<Person id="42" registered="2024-07-01">
  <FullName>Ivan Ivanov</FullName>
  <EMails>
    <Email>user@mail.com</Email>
  </EMails>
  <UserAddresses>
    <Address City="Neon city">вул. Замкова, буд. 1</Address>
    <Address City="North Cave">вул. Крайня буд. 12</Address>
  </UserAddresses>
</Person>

Видно, що в кожному <Address> атрибут City містить назву міста, а саму адресу вказано як текст усередині елемента.

5. Поширені помилки та нюанси

Якщо ви серіалізуєте складний об’єкт і не бачите у XML очікуваних змін — найімовірніше, забули правильно вказати атрибут. Іноді буває, що властивості/поля залишаються без маркування. Тоді .NET просто обирає поведінку за замовчуванням.

Ще одна пастка — ім’я колекції. Якщо колекція (наприклад, масив рядків) не позначена ні [XmlArray], ні [XmlArrayItem], то елементи запишуться з тегом string (як видно в початковому прикладі вище). Щоб цього не було, завжди явно задавайте імена тегів через атрибути.

Будьте обережні з властивостями типу «getter‑only» (лише для читання) — такі властивості за замовчуванням не десеріалізуються. Намагайтеся залишати публічний set.

Деякі типи (наприклад, словники або інтерфейси) стандартний XmlSerializer не серіалізує або вони потребують окремої обробки.

2
Задача
C# SELF, 48 рівень, 1 лекція
Недоступна
Серіалізація колекції з використанням XmlArray та XmlArrayItem
Серіалізація колекції з використанням XmlArray та XmlArrayItem
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ