XML DOM
Что такое XML DOM
DOM (Document Object Model) — программный интерфейс к дереву XML (и HTML). Парсер загружает файл в память и предоставляет объекты для чтения, изменения, добавления и удаления узлов.
В экосистеме XML DOM тесно связан с XPath (глава) и отличается от потоковых API (SAX, XmlReader в .NET), которые не держат всё дерево в памяти.
| Подход | Память | Удобство |
|---|---|---|
| DOM | Весь документ | Произвольная навигация, правки |
| SAX / XmlReader | Поток | Большие файлы, только последовательное чтение |
LINQ to XML (XDocument) | Дерево, современный API .NET | Удобнее классического DOM |
Основы разметки: XML.
Конвейер обработки XML
Программа редко "просто читает файл". Типичная схема (по модели XML-процессора):
| Стадия | Ответственность |
|---|---|
| Парсер | Кодировка, токены, дерево; ошибка → номер строки |
| Validator | Соответствие XSD/DTD (опционально) |
| API доступа | DOM (всё в памяти), SAX/StAX (события), LINQ to XML |
| Трансформация | XSLT или обход дерева в коде |
SAX — событийная модель
SAX (Simple API for XML) не строит полное дерево в памяти. Парсер вызывает обработчики (handlers) по мере чтения:
| Событие | Когда |
|---|---|
startElement | Открывающий тег |
characters | Текстовые данные |
endElement | Закрывающий тег |
startDocument / endDocument | Начало и конец |
// Java JAXP — иллюстрация идеи
DefaultHandler handler = new DefaultHandler() {
@Override
public void startElement(String uri, String localName, String qName, Attributes attrs) {
// обработка открытия тега
}
};
SAXParserFactory.newInstance().newSAXParser().parse(new File("big.xml"), handler);
В .NET аналог — XmlReader (последовательное чтение без DOM):
using var reader = XmlReader.Create("big.xml");
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "book")
{
string id = reader.GetAttribute("id");
}
}
| Критерий | DOM / XDocument | SAX / XmlReader |
|---|---|---|
| Память | Весь документ | O(1) относительно размера файла |
| Навигация | Произвольная, XPath | Только вперёд по потоку |
| Изменение дерева | Да | Нет (только генерация нового) |
| Типичный размер | До десятков МБ | Гигабайтные логи и выгрузки |
Для больших XML на сервере сначала оцените потоковый API; DOM удобен для небольших конфигов и сообщений интеграции.
Узлы XML DOM
Каждый фрагмент документа — узел с типом nodeType (в W3C DOM):
| Тип | Константа (типично) | Пример |
|---|---|---|
| Element | ELEMENT_NODE (1) | <book> |
| Attribute | ATTRIBUTE_NODE (2) | id="1" |
| Text | TEXT_NODE (3) | Война и мир |
| CDATA Section | CDATA_SECTION_NODE (4) | <![CDATA[...]]> |
| Entity Reference | ENTITY_REFERENCE_NODE (5) | & |
| Entity | ENTITY_NODE (6) | в DTD |
| Processing Instruction | PROCESSING_INSTRUCTION_NODE (7) | <?xml-stylesheet ...?> |
| Comment | COMMENT_NODE (8) | {/* ... */} |
| Document | DOCUMENT_NODE (9) | корень API |
| Document Type | DOCUMENT_TYPE_NODE (10) | <!DOCTYPE ...> |
| Document Fragment | DOCUMENT_FRAGMENT_NODE (11) | временный контейнер |
| Notation | NOTATION_NODE (12) | в DTD |
Доступ к узлам
После разбора документа доступны свойства и методы (имена близки в JavaScript, Java, .NET XmlDocument):
| Операция | Идея |
|---|---|
documentElement | Корневой элемент XML |
getElementsByTagName('book') | Коллекция по локальному имени (без учёта NS в старых API) |
getAttribute('id') | Значение атрибута элемента |
childNodes | Все дочерние узлы, включая текст и комментарии |
firstChild / lastChild | Первый / последний дочерний |
parentNode | Родитель |
nodeValue | Текст для текстовых узлов; для элементов — не всегда то, что ожидают новички |
В .NET для новых проектов чаще используют XElement, XDocument; классический DOM — XmlDocument + SelectNodes с XPath.
Пример чтения (C# / XmlDocument):
var doc = new XmlDocument();
doc.Load("library.xml");
XmlNodeList books = doc.SelectNodes("/library/book");
foreach (XmlNode book in books)
{
string title = book.SelectSingleNode("title")?.InnerText;
string id = book.Attributes["id"]?.Value;
}
Тот же выбор через XPath описан в главе XPath.
NodeList и NodeMap
NodeList — упорядоченная коллекция узлов (результат getElementsByTagName, XPath). Может быть "живой": изменение дерева меняет список.
NamedNodeMap — атрибуты элемента, доступ по имени или индексу:
// Иллюстрация в стиле W3C DOM (браузер / legacy)
const attr = element.attributes.getNamedItem('id');
Важно: текст внутри <title>Война</title> часто представлен как дочерний текстовый узел, а не как "значение элемента" одной строкой — при обходе childNodes появляются "лишние" текстовые узлы с пробелами и переводами строк.
Обход дерева узлов
Три классических способа:
1. По дочерним узлам (рекурсия)
function traverse(node):
обработать(node)
for each child in node.childNodes:
traverse(child)
2. Обход в глубину (DFS)
Сначала вниз по ветке, затем к соседям — естественен для DOM.
3. TreeWalker / NodeIterator (W3C)
Фильтры по типу узла (только элементы, без пустого текста). В .NET аналог — собственные обходы или LINQ по Descendants().
Навигация по узлам
| Свойство / метод | Направление |
|---|---|
parentNode | Вверх |
childNodes | Вниз на один уровень |
nextSibling / previousSibling | Соседи на том же уровне |
XPath /library/book[2]/title | Декларативный путь (XPath) |
Смешанный контент — между <book> и <title> парсер может вставить текстовый узел с переводом строки — при nodeValue элемента book получится склейка текстов потомков, но не всегда так, как ожидается без InnerText / нормализации.
Создание и вставка (редактирование дерева):
createElement,createTextNode,setAttribute;appendChild,insertBefore,removeChild,replaceChild.
После изменений документ сериализуют обратно в файл (Save в .NET).
DOM в браузере и на сервере
| Контекст | API |
|---|---|
| Браузер | DOMParser, document.implementation.createDocument — для XML; HTML использует тот же DOM, но другие правила |
| Java | DocumentBuilder, org.w3c.dom |
| .NET | XmlDocument, предпочтительно XDocument |
| Python | xml.dom.minidom (реже — lxml etree) |
Для больших XML на сервере предпочтительны потоковые парсеры; DOM загружает весь файл в RAM.
Связь с XSLT и валидацией
- XSLT-процессор внутри строит дерево исходника (часто DOM-подобное).
- Валидация XSD может выполняться до или после построения DOM (XML, справочник).
- Преобразование через XSLT: глава XSLT — DOM исходника напрямую не обязателен менять.
Загрузите небольшой XML в IDE или скриптом, выведите список имён всех дочерних элементов корня и значения каждого атрибута id у элементов book.