Learning Platform
Глоссарий Troubleshooting
Урок 07.03 · 30 мин
Средний
XMLNamespacesXSDSAXDOMXSLTSOAPOOXMLRSS

XML: Формат и парсинг

XML — обзор

XML (eXtensible Markup Language) — текстовый формат разметки данных, стандартизированный W3C в 1998. XML был доминирующим форматом обмена данными в 2000-х: SOAP web-сервисы, конфигурации (Maven, Ant, Spring), документы (OOXML, ODF), синдикация (RSS, Atom). Сейчас во многих областях заменён JSON, но продолжает использоваться в корпоративных системах.

Ключевое отличие XML от JSON: расширяемость через namespace’ы и схемы. XML позволяет определять собственные теги, комбинировать словари (namespaces), и валидировать структуру через XSD. Цена — многословность и сложность парсинга.

XML vs JSON: архитектурные различия
JSONJSON: минималистичный формат. 6 типов, {} и [] для структуры, нет атрибутов, нет namespace'ов. Одна спецификация (RFC 8259), один парсер.
XMLXML: расширяемый формат. Пользовательские теги, namespace'ы для комбинирования словарей, XSD для валидации, XSLT для трансформаций. Экосистема стандартов, но высокая сложность.

Структура XML документа

XML-документ состоит из элементов (tags), атрибутов, текстового содержимого, и необязательных деклараций:

Анатомия XML документа
XML DeclarationНеобязательная первая строка: <?xml version='1.0' encoding='UTF-8'?>. Указывает версию XML (1.0 или 1.1), кодировку (обычно UTF-8), и standalone (yes/no). В отличие от CSV, XML явно декларирует кодировку.
Root ElementКаждый XML-документ имеет ровно один корневой элемент. Все остальные элементы — его потомки. Нарушение → not well-formed XML.
Child ElementЭлемент с атрибутами и дочерними элементами. Открывающий тег <user id='1'>, закрывающий тег </user>. Атрибуты — пары key=value внутри открывающего тега.
Self-closingПустой элемент: <tag /> или <tag></tag>. Оба валидны. Self-closing сокращает запись для элементов без содержимого (аналог null).
Closing RootЗакрывающий тег корневого элемента. Каждый открывающий тег ДОЛЖЕН иметь закрывающий (well-formed XML). Несоответствие → parse error.

Encoding overhead: теги × 2

XML самый многословный из текстовых форматов — каждый элемент дублирует имя тега (открывающий + закрывающий):

XML Encoding Overhead: сравнение
ФорматФормат кодирования одной записи.
ЗаписьКак выглядит одна запись User(id=1, name=Alice, age=30).
БайтыКоличество байт на одну запись (без whitespace/pretty-print).
XMLXML: открывающий + закрывающий тег для каждого поля и каждой записи.
Каждое поле: <tag>value</tag> — имя тега повторяется дважды.
72 байта на одну запись. Теги = 56 байт (78%), данные = 9 байт (12%).
JSONJSON: ключи в кавычках, скобки, двоеточия, запятые.
Ключи повторяются, но без дублирования (нет closing tag).
38 байт. Ключи + синтаксис = 29 байт (76%), данные = 9 байт (24%).
CSVCSV: только разделители.
Только данные и запятые. Header считается один раз на файл.
10 байт + header однократно. Минимальный overhead.
AvroAvro: бинарные значения, без ключей.
zigzag(1) + len('Alice') + 'Alice' + zigzag(30) = 8 байт.
8 байт. Чистые данные без overhead.

XML-запись в 9 раз больше Avro для тех же данных. При миллионах записей разница измеряется гигабайтами.

Namespace’ы: комбинирование словарей

Namespace’ы позволяют использовать элементы из разных XML-словарей в одном документе без конфликтов имён:

XML Namespaces: зачем и как
ПроблемаБез namespace'ов два XML-словаря с элементом <table> (HTML table и database table) конфликтуют. Парсер не знает, какой <table> имеется в виду.
Решение: namespace URINamespace URI — уникальный идентификатор словаря. Привязывается к prefix через xmlns. URI не обязан быть URL — это просто уникальная строка.
Prefix bindingxmlns:prefix='URI' — привязка prefix к namespace URI. Все элементы с этим prefix принадлежат данному namespace. Default namespace: xmlns='URI' без prefix.
ScopeNamespace declaration действует на элемент и всех потомков. Можно переопределить во вложенном элементе. Парсер отслеживает стек namespace bindings.
NOTE

Namespace URI — не URL. http://www.w3.org/1999/xhtml — это идентификатор, а не адрес. Парсер не загружает эту страницу. Но по конвенции URI часто является URL, указывающим на документацию namespace.

XSD: валидация структуры

XML Schema Definition (XSD) — язык описания структуры XML-документов. Более мощный, чем DTD: поддерживает типы данных, ограничения, наследование:

XSD: ключевые конструкции
КонструкцияЭлемент XSD-схемы.
ОписаниеЧто описывает данная конструкция.
АналогАналог в Avro/Protobuf/JSON Schema.
xs:elementxs:element — определение элемента с именем и типом. Аналог field в record.
Определяет элемент: имя, тип, minOccurs, maxOccurs.
Avro: field в record. Protobuf: field в message. JSON Schema: property.
xs:complexTypexs:complexType — составной тип с дочерними элементами и/или атрибутами.
Тип с вложенной структурой: sequence, choice, all дочерних элементов.
Avro: record. Protobuf: message. JSON Schema: object.
xs:simpleTypexs:simpleType — примитивный тип с ограничениями (restrictions): pattern, minInclusive, enumeration.
Примитив с валидацией: regex pattern, min/max, enumeration, длина строки.
Avro: нет (типы без constraints). JSON Schema: format, pattern, minimum.
xs:sequencexs:sequence — упорядоченная последовательность дочерних элементов.
Дочерние элементы должны появляться в указанном порядке.
Avro: поля в record упорядочены. Protobuf: порядок не гарантирован (field numbers).

SAX vs DOM: модели парсинга

XML имеет две принципиально разные модели парсинга:

SAX vs DOM: архитектура парсинга
SAX (Simple API for XML)Event-driven, streaming парсер. Читает XML последовательно, вызывая callbacks на каждый элемент: startElement, endElement, characters. Не строит дерево — минимальный расход памяти. O(1) memory.
DOM (Document Object Model)Загружает весь XML в память как дерево объектов. Можно навигировать в любом направлении: parent, children, siblings. Поддерживает XPath-запросы. O(n) memory — для 1 GB XML нужно 3-5 GB RAM.
SAX vs DOM: read path
SAX Read PathSAX: парсер последовательно читает XML, вызывая callbacks. Приложение получает события и решает, что делать. Парсер не хранит предыдущие элементы — O(1) memory.
DOM Read PathDOM: парсер читает весь XML и строит дерево узлов в памяти. После загрузки — произвольный доступ через API. Каждый элемент, атрибут, текстовый узел = объект в памяти.
SAX vs DOM: когда что использовать
КритерийПо какому критерию выбирать парсер.
SAXEvent-driven streaming парсер.
DOMIn-memory tree парсер.
Размер файлаОбъём XML-документа.
SAX: любой размер. 10 GB XML — без проблем. Память не зависит от размера.
DOM: размер ограничен RAM. 1 GB XML ≈ 3-5 GB RAM. 10 GB XML — OutOfMemoryError.
НавигацияНужен ли произвольный доступ к элементам.
SAX: только вперёд. Нельзя вернуться к предыдущему элементу. Для обратной навигации — хранить состояние вручную.
DOM: XPath, parent/child/sibling навигация. getElementById, querySelector.
Типичный use caseДля чего обычно используется.
ETL: извлечь записи из большого XML дампа. Streaming: обработать XML из сетевого потока. Log parsing.
XSLT-трансформации (нужно всё дерево). Конфигурации (маленькие файлы). Редактирование XML (modify + save).
TIP

Существует третий подход — StAX (Streaming API for XML): pull-based парсер. В отличие от SAX (push: парсер вызывает callbacks), StAX (pull: приложение запрашивает следующий event). StAX проще для сложной логики обработки, потому что контроль потока у приложения, а не у парсера.

XSLT: трансформации XML

XSLT (eXtensible Stylesheet Language Transformations) — язык для преобразования XML-документов в другие форматы (XML, HTML, текст). XSLT сам написан на XML:

XSLT Pipeline: XML → Transform → Output
Source XMLИсходный XML-документ с данными. XSLT-процессор загружает его в DOM (нужно всё дерево для XPath-навигации).
XSLT StylesheetXSLT Stylesheet: набор template rules. Каждый template match='XPath' определяет, как трансформировать matching элементы. XSLT-процессор: Saxon, Xalan, libxslt.
Output (XML/HTML/text)Результат трансформации: другой XML, HTML, plain text. XSLT 3.0 поддерживает вывод в JSON.
XPathXPath — язык запросов для навигации по XML-дереву. Используется внутри XSLT для выбора элементов. Примеры: /users/user[@role='admin'], //name/text(), count(//user).
Template matchingКаждый xsl:template match='pattern' определяет трансформацию для matching элементов. XSLT-процессор обходит source XML и применяет matching templates.

Почему XML жив: SOAP, config, документы

Несмотря на доминирование JSON для API, XML остаётся стандартом в нескольких нишах:

XML: живые use cases (2024+)
ОбластьГде XML до сих пор является стандартом.
ПримерыКонкретные технологии и стандарты.
Почему не JSONПочему эти области не мигрировали на JSON.
Enterprise APIEnterprise-интеграции: SOAP, WSDL, WS-Security. Банки, страховые, госсектор — всё на SOAP.
SOAP 1.2, WSDL, WS-Security, WS-ReliableMessaging. SAP, Oracle EBS, банковские системы.
Legacy: миллиарды строк кода. WS-Security: нет JSON-аналога для enterprise security. Регуляторные требования.
Build / ConfigКонфигурации: Maven POM, Spring XML, Android manifest, Ant build files.
pom.xml (Maven), web.xml (Servlet), AndroidManifest.xml, .csproj (MSBuild), hibernate.cfg.xml.
Существующие экосистемы. XSD-валидация config при загрузке. IDE-поддержка (autocomplete по XSD).
ДокументыДокументы: Office Open XML (.docx/.xlsx/.pptx), SVG, MathML, RSS/Atom.
OOXML (.docx = ZIP с XML внутри), SVG (векторная графика), MathML (математические формулы), EPUB.
XML идеален для документов: mixed content (текст + разметка), namespace'ы для комбинирования словарей.
SyndicationSyndication: RSS 2.0, Atom 1.0. Каждый подкаст-клиент, news aggregator использует RSS.
RSS 2.0 (XML), Atom 1.0 (XML). Podcast RSS feeds. Apple Podcasts, Spotify — парсят RSS.
Стандарт зафиксирован. Миллионы feeds. Менять формат = ломать экосистему.
OOXML: XML внутри .docx
file.docx (ZIP).docx файл — это ZIP-архив. Внутри: XML-файлы для содержимого, стилей, связей. Можно unzip и прочитать.
word/document.xmlОсновной контент документа. Параграфы, таблицы, изображения — всё описано в XML с namespace'ом wordprocessingml.
word/styles.xmlСтили документа: шрифты, размеры, отступы. Отдельный XML-файл.
word/_rels/document.xml.relsСвязи между частями документа: images, hyperlinks, embedded objects.
docProps/core.xmlМетаданные: автор, дата создания, revision. Dublin Core namespace.

XML в data pipelines

XML в data engineering — обычно входной формат из legacy-систем, который нужно конвертировать:

XML ETL Pipeline
XML SourceИсточник XML: SOAP-ответ от банковской системы, XML-дамп из SAP, RSS feed, конфигурационный файл. Часто — единственный доступный формат.
SAX Parse + ExtractSAX/StAX парсинг для больших файлов. Извлечение записей без загрузки всего документа в память. XPath для навигации по структуре.
Flatten / DenormalizeFlatten: вложенная XML-структура → плоские строки. Denormalize: nested elements → columns. Это самый сложный этап — XML-структура произвольная.
Parquet / DeltaРезультат: данные в Parquet/Delta Lake. Columnar access, типизация, compression. XML больше не нужен.
WARNING

Spark XML reader (spark-xml library) загружает XML через com.databricks.spark.xml. Ключевая проблема: XML не splittable по byte offset (вложенность + namespace’ы). Spark читает каждый XML-файл одним executor’ом. Для параллелизма: разбейте XML на много маленьких файлов перед ingestion.

Итог

XML — мощный, но тяжёлый формат. Его преимущества (namespace’ы, XSD-валидация, XSLT-трансформации, mixed content) востребованы в документах, конфигурациях и enterprise-интеграциях. Для data engineering XML — обычно legacy-источник, требующий конверсии в columnar формат. Overhead XML (теги × 2, атрибуты, пробелы) делает его самым неэффективным форматом хранения данных.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. XML-запись <user><id>1</id><name>Alice</name><age>30</age></user> занимает 72 байта. Эквивалентная Avro-запись (zigzag int + length-prefixed string + zigzag int) — 8 байт. Основная причина 9× разницы?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5