JavaRush /Курсы /C# SELF /Продвинутая работа с XML: ...

Продвинутая работа с XML: XmlDocument и XPath

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

1. Введение

Представьте себе гигантский XML, который прислал вам какой-нибудь SOAP‑сервис или банковский API. В нём сотни вложенных элементов, неоднородные структуры, а вам нужно найти всего одну-единственную сумму платежа за прошлый месяц. Приём с сериализацией тут не подходит — слишком громоздко и заранее неизвестно, куда смотреть. Нужна "лупа" для XML-дерева и умение быстро бегать по его ветвям.

Именно для таких случаев в .NET уже много лет есть мощный инструмент — класс XmlDocument, дополняемый языком запросов XPath. Сегодня вы научитесь:

  • Загружать XML в DOM-дерево;
  • Перемещаться и находить нужные элементы вручную и с помощью XPath;
  • Модифицировать содержимое XML-документа;
  • Добавлять, удалять и изменять узлы;
  • Использовать XPath для сложных выборок.

2. Что такое XmlDocument?

XmlDocument — класс из пространства имён System.Xml. Это реализация DOM (Document Object Model) — представления всего XML-файла в виде дерева объектов в памяти.

Немного теории и аналогий

Если XmlSerializer похож на конструктор LEGO, когда вы превращаете объект в набор кубиков и обратно, то XmlDocument — это как работа с настоящим деревом: у него есть корень, ветви (элементы), листья (текстовые узлы), и вы можете ходить по этому дереву, добавлять ветви, срезать листья и пересаживать их куда хотите.

Простая загрузка XML-документа

Пусть у нас есть такой XML:

<users>
  <user id="1">
    <name>Ольга</name>
    <age>28</age>
  </user>
  <user id="2">
    <name>Игорь</name>
    <age>35</age>
  </user>
</users>

Загрузим его в память:

using System.Xml;

string xml = @"
<users>
  <user id='1'>
    <name>Ольга</name>
    <age>28</age>
  </user>
  <user id='2'>
    <name>Игорь</name>
    <age>35</age>
  </user>
</users>";

XmlDocument doc = new XmlDocument();
doc.LoadXml(xml); // или doc.Load("путь_к_файлу.xml");

После вызова LoadXml (или Load, если XML из файла) у вас есть полный доступ к содержимому документа.

3. Навигация по DOM-дереву

Структура DOM-дерева

Каждый XML-документ после загрузки становится деревом, состоящим из узлов различных типов:

Тип узла Класс в .NET Пример
Document XmlDocument
<users>...</users>
Element XmlElement
<user>, <name>
Attribute XmlAttribute
id="1"
Text XmlText Ольга, 28
Comment XmlComment
<!-- комментарий -->

Чтобы обращаться к нужным элементам — придётся "ходить по дереву", используя свойства ChildNodes, Attributes, ParentNode и т. д.

Получаем корневой элемент

XmlElement root = doc.DocumentElement;
Console.WriteLine(root.Name); // users

Перебираем дочерние элементы

foreach (XmlNode node in root.ChildNodes)
{
    if (node is XmlElement user)
    {
        // user — это <user id="...">...</user>
        string id = user.GetAttribute("id");
        string name = user["name"].InnerText;
        string age = user["age"].InnerText;
        Console.WriteLine($"Пользователь {id}: {name}, возраст {age}");
    }
}

ВАЖНО: Доступ user["name"] возможен только если среди непосредственных потомков есть элемент <name>.

Доступ к атрибутам и тексту

var firstUser = root.FirstChild as XmlElement;
string id = firstUser.GetAttribute("id"); // "1"
string name = firstUser["name"].InnerText; // "Ольга"

4. Модификация XML-документа

Изменение значения

Пусть Ольга внезапно решила "омолодиться":

var olga = root.FirstChild as XmlElement;
olga["age"].InnerText = "22"; // теперь <age>22</age>

Добавление нового пользователя

XmlElement newUser = doc.CreateElement("user");
newUser.SetAttribute("id", "3");

XmlElement name = doc.CreateElement("name");
name.InnerText = "Василиса";
XmlElement age = doc.CreateElement("age");
age.InnerText = "19";

newUser.AppendChild(name);
newUser.AppendChild(age);
root.AppendChild(newUser);

Удаление элемента

Удалим второго пользователя ("Игорь"):

XmlNode userToDelete = root.SelectSingleNode("user[@id='2']");
if (userToDelete != null)
    root.RemoveChild(userToDelete);

Сохранение изменений

doc.Save("users_updated.xml");
// Или doc.OuterXml — чтобы получить строку с XML

5. XPath — язык поиска и выборки в XML

Работа с DOM-деревом становится утомительной, если нужно искать элементы "по условию" — например, всех пользователей старше 25 лет. Для этого и существует XPath — язык навигации по XML-дереву.

Базовое использование XPath

XPath-запросы можно выполнять через методы SelectSingleNode (вернёт первый найденный узел) и SelectNodes (вернёт коллекцию узлов).

Пример: найти пользователя с id = 1

XmlNode user = root.SelectSingleNode("user[@id='1']");
Console.WriteLine(user["name"].InnerText); // Ольга

Пример: найти всех пользователей старше 25 лет

XmlNodeList nodes = root.SelectNodes("user[age>25]");
foreach (XmlNode u in nodes)
{
    Console.WriteLine(u["name"].InnerText); // Выведет Ольга и Игорь (если Игоря ещё не удалили)
}

XPath — краткий синтаксис

XPath выражение Что сделает
/users/user
Все элементы <user> в корне <users>
user[@id='3']
Пользователь с id=3
user[age>25]
Пользователи старше 25 лет
user/name
Все элементы <name> всех пользователей
user[last()]
Последний <user>
user[position()<3]
Первые два <user>

Больше примеров и синтаксиса: Документация по XPath.

Ещё пример: выборка по вложенным элементам

Допустим, XML сложнее:

<library>
  <book>
    <title>Повелитель мух</title>
    <author>
      <firstname>Вильям</firstname>
      <lastname>Голдинг</lastname>
    </author>
  </book>
  <book>
    <title>В сторону Свана</title>
    <author>
      <firstname>Марсель</firstname>
      <lastname>Пруст</lastname>
    </author>
  </book>
</library>
XmlDocument doc = new XmlDocument();
doc.LoadXml(libraryXml);
XmlNodeList authors = doc.SelectNodes("/library/book/author/lastname");
foreach (XmlNode lastName in authors)
    Console.WriteLine(lastName.InnerText);

Что будет выведено:

Голдинг
Пруст

6. XPath: фильтрация, логика, вычисления

Логические фильтры

Вывести имена всех пользователей, чей возраст между 18 и 30:

// [age>=18 and age<=30]
XmlNodeList youngUsers = root.SelectNodes("user[age>=18 and age<=30]");
foreach (XmlNode u in youngUsers)
    Console.WriteLine(u["name"].InnerText);

Работа с атрибутами

Атрибуты выбираются через @. Найдём пользователей, у которых id начинается на "1":

XmlNodeList nodes = root.SelectNodes("user[starts-with(@id, '1')]");

Подсчёт количества узлов

Напрямую в C# методе SelectNodes нельзя использовать функцию count() — он возвращает коллекцию, а не число. Но можно сделать так:

int count = root.SelectNodes("user").Count;

Вложенные фильтры

XmlNodeList nodes = doc.SelectNodes("/library/book[author/lastname='Голдинг']");

7. XPath и пространства имён

Если ваш XML использует пространства имён (xmlns), всё становится на порядок веселее! Для таких случаев используйте XmlNamespaceManager:

<catalog xmlns="http://books.example.com">
  <book>
    <title>Алгоритмы</title>
  </book>
</catalog>
doc.LoadXml(xml);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("b", "http://books.example.com");

XmlNodeList books = doc.SelectNodes("/b:catalog/b:book", nsmgr);
foreach (XmlNode book in books)
    Console.WriteLine(book.SelectSingleNode("b:title", nsmgr)?.InnerText);

8. Модификация XML с помощью DOM

Добавление узлов "на лету"

Добавим новый элемент <email> в первого пользователя:

XmlElement email = doc.CreateElement("email");
email.InnerText = "olga@gmail.com";
olga.AppendChild(email);

Изменение атрибутов

olga.SetAttribute("id", "99"); // Теперь id="99"

Удаление узлов с помощью XPath

XmlNodeList nodesToRemove = root.SelectNodes("user[age<20]");
foreach (XmlNode node in nodesToRemove)
    root.RemoveChild(node);

9. Полезные нюансы

Поиск и массовое изменение структуры

Предположим, вам приходит большой XML с разными элементами <record type="...">. Нужно оставить только те, у которых в атрибуте type стоит "customer", и добавить каждому дочерний элемент <status>active</status>.

XmlNodeList customerNodes = root.SelectNodes("record[@type='customer']");
foreach (XmlElement record in customerNodes)
{
    XmlElement status = doc.CreateElement("status");
    status.InnerText = "active";
    record.AppendChild(status);
}

Дерево узлов XML (DOM)


users
├─ user (id="1")
│   ├─ name ("Ольга")
│   └─ age ("28")
├─ user (id="2")
│   ├─ name ("Игорь")
│   └─ age ("35")

Пример выбора по XPath

XPath-запрос Что вернёт
/users/user[1]
Первый пользователь (id="1")
/users/user[last()]
Последний пользователь (id="2")
/users/user[age>30]
Все старше 30 лет (Игорь)
/users/user[@id='2']
Пользователь с id="2"

Практическое применение

  • Интеграции со "старыми" API. Во многих госкорпорациях и банках SOAP/XML всё ещё правят балом. Возможность быстро найти и поменять что-то в большом XML-ответе может спасти десятки часов.
  • Миграция данных. При переходе с одной системы на другую зачастую нужно парсить XML и делать сложные выборки, преобразования и массовые изменения.
  • Импорт-экспорт в Excel. Во многих B2B продуктах всё ещё на вход приходит XML. Там XPath — быстрый способ достать нужное без создания большой модели данных.

10. Ошибки, нюансы и типичные ловушки

NullReferenceException: Если попытаться обратиться к несуществующему элементу, например, el["something"].InnerText, а такого дочернего элемента нет вовсе. Всегда проверяйте на null.

XPath и вложенность: Помните, что выражение без начального / ищет среди потомков текущего узла, а с / — от корня документа. Разные точки отсчёта могут привести к отсутствию результата, хотя данные есть.

Работа с пространствами имён: Если не использовать XmlNamespaceManager, XPath-запросы к XML с xmlns будут возвращать пусто.

Модификация во время перебора: Если хотите удалять узлы по результатам SelectNodes, сначала сохраните их в массив, а потом итерируйте — иначе коллекция изменится по ходу перебора, и могут возникнуть ошибки.

Отличие текстовых и элементных узлов: Между элементами могут быть текстовые узлы — пробелы и переводы строк, которые XML считает содержимым. Для строгой выборки используйте только XmlElement или фильтруйте по NodeType.

2
Задача
C# SELF, 48 уровень, 2 лекция
Недоступна
Поиск элементов с помощью XPath
Поиск элементов с помощью XPath
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Aleksei Perchukov Уровень 66
15 октября 2025
Обновите ссылку на доку по XPath - там сейчас 404