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 без параметрів).

• Властивості, що серіалізуються, — публічні: потрібні обидва аксесори: get і set. Приватні члени ігноруються.

• Властивості лише для читання, а також статичні й абстрактні властивості не підтримуються.

Якщо порушити правила, отримаєте 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 — зазначайте кодування явно.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ