Привет, Хабр!

Обработка XML-данных остаётся актуальной задачей несмотря на появление более крутых технологий для работы с данными. XML — это все еще гибкий и мощный инструмент для структурирования данных, который используется в самых разнообразных приложениях.

В статье рассмотрим как работать с XML в C#.

Работа с XML в C#

Начнем с базы.

XmlDocument — это класс, который позволяет работать с XML в доменной модели. Можно загружать, изменять и сохранять XML-документы. Читать XML можно через методыLoad и LoadXml, которые считывают данные из файлов или строк. Создание новых узлов и атрибутов происходит с помощью методов CreateElement и CreateAttribute, после чего элементы добавляются в документ с помощью AppendChild.

Cоздадим XML-документ, добавим в него элементы и атрибуты, а затем сохраним его:

using System;
using System.Xml;

class Program
{
    static void Main()
    {
        // новый XML-документ
        XmlDocument doc = new XmlDocument();

        // корневой элемент
        XmlElement root = doc.CreateElement("users");
        doc.AppendChild(root);

        // новый элемент
        XmlElement user = doc.CreateElement("user");
        root.AppendChild(user);

        // атрибут для элемента user
        XmlAttribute attr = doc.CreateAttribute("id");
        attr.Value = "1";
        user.Attributes.Append(attr);

        // вложенные элементы в элемент user
        XmlElement firstName = doc.CreateElement("firstName");
        firstName.InnerText = "John";
        user.AppendChild(firstName);

        XmlElement lastName = doc.CreateElement("lastName");
        lastName.InnerText = "Doe";
        user.AppendChild(lastName);

        // сейвим XML-документ в файл
        doc.Save("users.xml");
    }
}

Но есть способы получше.

XmlReader и XmlWriter

XmlReader и XmlWriter предоставляют более производительные альтернативы для чтения и записи XML по сравнению с DOM, т.к они работают в потоковом режиме. XmlReader читает XML поэлементно.

Пример XmlReader:

using System;
using System.Xml;

class Program
{
    static void Main()
    {
        // создаем XmlReader для чтения файла
        using (XmlReader reader = XmlReader.Create("example.xml"))
        {
            while (reader.Read()) // чтение некст элемента
            {
                if (reader.NodeType == XmlNodeType.Element && reader.Name == "name")
                {
                    Console.WriteLine(reader.ReadElementContentAsString()); // читаем содержимое элемента <name>
                }
            }
        }
    }
}

Здесь юзаем XmlReader для поэлементного чтения XML-файла. Фильтруем элементы по типу узла XmlNodeType.Element и имени name, чтобы извлечь информацию только из тех элементов, которые нас интересуют.

Пример XmlWriter:

using System;
using System.Xml;

class Program
{
    static void Main()
    {
        // создаем XmlWriter
        using (XmlWriter writer = XmlWriter.Create("output.xml"))
        {
            writer.WriteStartDocument(); // начало документа
            writer.WriteStartElement("users"); // начало корневого элемента <users>

            writer.WriteStartElement("user"); // начало элемента <user>
            writer.WriteElementString("name", "Ivan"); //добавление <name>
            writer.WriteEndElement(); // закрытие элемента <user>

            writer.WriteEndElement(); // закрытие корневого элемента <users>
            writer.WriteEndDocument(); // закрытие документа
        }
    }
}

Используем XmlWriter для создания нового XML-файла с простой структурой. Стартуем с создания корневого элемента <users>, добавляем в него дочерний элемент <user> с вложенным элементом <name>, и последовательно закрываем все открытые элементы и документ.

XDocument

Также существует XDocument и связанные с ним классы XElement, XAttribute.

XElement — это отдельный элемент в XML-документе и он позволяет управлять содержимым элемента, включая его вложенные элементы, текст и атрибуты.

XAttribute служит для работы с атрибутами XML-элементов. Атрибуты представляют собой пары имя-значение, которые прикрепляются к элементам.

Cоздадим простой XML-документ, который содержит инфу о нескольких юзерах, каждый из которых имеет уникальный идентификатор и имя:

using System;
using System.Xml.Linq;

class Program
{
    static void Main()
    {
        // новый XML-документа
        XDocument xmlDoc = new XDocument(
            new XDeclaration("1.0", "utf-8", null),
            new XElement("users", // корневой элемент
                new XElement("user", // дочерний элемент
                    new XAttribute("id", "1"), // атрибут элемента user
                    new XElement("name", "Ivan")
                ),
                new XElement("user",
                    new XAttribute("id", "2"),
                    new XElement("name", "Kolya")
                )
            )
        );

        Console.WriteLine(xmlDoc.ToString());

        // сохранение XML-документа в файл
        xmlDoc.Save("users.xml");
    }
}

XDocument создаёт новый XML-документ.

XElement используется для создания элементов users и user. Элемент users служит корневым элементом, а user — дочерним элементом, представляющим пользователя.

XAttribute применяется для добавления атрибутов к элементу user, в данном случае это идентификатор пользователя.

LINQ

Можно интегрировать LINQ с XML и сделать код более читабельным.

Например, так можно сделать чтение и запрос данных:

using System;
using System.Linq;
using System.Xml.Linq;

class Program
{
    static void Main()
    {
        XDocument doc = XDocument.Load("books.xml");

        var books = from book in doc.Descendants("book")
                    where (int)book.Attribute("id") == 1
                    select new
                    {
                        Title = book.Element("title").Value,
                        Author = book.Element("author").Value
                    };

        foreach (var book in books)
        {
            Console.WriteLine($"Title: {book.Title}, Author: {book.Author}");
        }
    }
}

А так сделать агрегацию данных из XML:

using System;
using System.Xml.Linq;
using System.Linq;

class Program
{
    static void Main()
    {
        XDocument doc = XDocument.Load("books.xml");

        var bookCount = doc.Descendants("book").Count(); // cчитаем количество книг
        var maxId = doc.Descendants("book").Max(book => (int)book.Attribute("id")); // Находим максимальный id

        Console.WriteLine($"Total books: {bookCount}, Max ID: {maxId}");
    }
}

Десериализация XML и обработка исключений

Для начала десериализации нужно создать класс, струк��ура которого соответствует структуре XML документа. Этот класс должен содержать поля или свойства, соответствующие элементам XML.

Создаем экземпляр XmlSerializer, указав тип объекта, который нужно десериализовать:

XmlSerializer serializer = new XmlSerializer(typeof(MyClass));

Также используемXmlSerializer для чтения XML из файла или потока и его преобразования в объект. Чаще всего используется StreamReader или StringReader:

using (StreamReader reader = new StreamReader("path_to_file.xml"))
{
    MyClass myObject = (MyClass)serializer.Deserialize(reader);
}

При десериализации могут возникать различные исключения, например, InvalidOperationException при несоответствии XML схемы ожидаемому классу или XmlException при синтаксических ошибках в XML. Обрабатывать эти исключения можно с помощью блоков try-catch:

try
{
    using (StreamReader reader = new StreamReader("path_to_file.xml"))
    {
        MyClass myObject = (MyClass)serializer.Deserialize(reader);
    }
}
catch (InvalidOperationException ex)
{
    Console.WriteLine($"Invalid XML format: {ex.Message}");
}
catch (XmlException ex)
{
    Console.WriteLine($"XML Parsing Error at line {ex.LineNumber}: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"General error: {ex.Message}");
}

Рассмотрим класс Person и XML файл, который содержит данные о человеке:

<Person>
    <Name>Ivan</Name>
    <Age>30</Age>
</Person>

Соответствующий класс Person в C# будет выглядеть так:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Десериализация этого XML в объект Person с XmlSerializer выглядит так:

string xmlData = @"<Person><Name>John Doe</Name><Age>30</Age></Person>";
using (StringReader stringReader = new StringReader(xmlData))
{
    XmlSerializer serializer = new XmlSerializer(typeof(Person));
    Person person = (Person)serializer.Deserialize(stringReader);
    Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
}

XML схемы и валидация в C#

XML схемы — это формальное описание структуры XML документа, в которым описаны все ограничения на содержимое и структуру элементов и атрибутов. Так можно автоматом проверять, что XML документы соответствуют заданным стандартам.

XML схема обычно определяет элементы и атрибуты, которые могут появляться в документе, их типы данных, и другие ограничения. Например, схема для описания книг в библиотеке может выглядеть так:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="library">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="book" maxOccurs="unbounded">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="title" type="xs:string"/>
                            <xs:element name="author" type="xs:string"/>
                            <xs:element name="isbn" type="xs:string"/>
                            <xs:element name="price" type="xs:decimal"/>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

Для валидации XML документа в соответствии с XSD схемой, можно использовать классы XmlReader и XmlReaderSettings из пространства имен System.Xml.Schema. Например:

using System;
using System.Xml;
using System.Xml.Schema;

class Program
{
    static void Main()
    {
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.Schemas.Add(null, "library.xsd");
        settings.ValidationType = ValidationType.Schema;

        XmlReader reader = XmlReader.Create("library.xml", settings);
        XmlDocument document = new XmlDocument();
        document.Load(reader);
        ValidationEventHandler eventHandler = new ValidationEventHandler(ValidationEventHandler);
        document.Validate(eventHandler);
    }

    static void ValidationEventHandler(object sender, ValidationEventArgs e)
    {
        if (e.Severity == XmlSeverityType.Error)
        {
            Console.WriteLine("Error: {0}", e.Message);
        }
        else if (e.Severity == XmlSeverityType.Warning)
        {
            Console.WriteLine("Warning: {0}", e.Message);
        }
    }
}

Таким образом можно обеспечить, что XML документы точно соответствуют определенным структурам и правилам.


Современные приложения иногда потребляют очень много памяти. Приглашаем вас на бесплатный урок, на котором будут рассмотрены основные приемы эффективной работы и экономии памяти в современных .net приложениях, и как помогают в этом ArrayPool, Span.