Для языка, который так любит XML, было довольно сложно найти эффективный и элегантный способ его синтаксического анализа.

Первоначально опубликовано на martinrichards.me.

Есть несколько способов синтаксического анализа XML?

Есть два способа работать с XML-документами: прочитать весь документ и построить полный объект DOM, представляющий документ, или прочитать документ как поток, извлекая из потока то, что вам нужно. Метод DOM подходит для небольших документов, таких как файлы конфигурации, но полностью разваливается при работе с документами любого реального размера.

Когда дело доходит до парсинга потоков в Java, парсер SAX кажется наиболее распространенным выбором. Большинство Ответов на переполнение стека и Руководств по ​​синтаксическому анализу больших файлов XML в Java указывают на синтаксический анализатор SAX. Единственная проблема парсера SAX заключается в том, что его API, управляемый событиями, очень неудобен для работы. Определение прослушивателя событий (обработчика) и ожидание его вызова удаляет ваш контроль над потоком. Мне нравится контролировать выполнение моей программы, и при работе с потоками я всегда предпочитал API на основе pull.

К счастью, как и большинство других вещей в программировании, я не первый, кто сталкивается с этими болевыми точками. StAX API был создан для решения этой проблемы, предоставляя API на основе вытягивания для потока XML.

Преимущества подхода лучше всего описаны в JavaDocs:

Анализ методом извлечения дает несколько преимуществ перед анализом методом извлечения при работе с потоками XML:

С помощью синтаксического анализа по запросу клиент управляет потоком приложения и может при необходимости вызывать методы синтаксического анализатора. Напротив, с обработкой push синтаксический анализатор управляет потоком приложения, и клиент может принимать только вызовы от анализатора.

Библиотеки синтаксического анализа по запросу могут быть намного меньше, а взаимодействие клиентского кода с этими библиотеками намного проще, чем с библиотеками push, даже для более сложных документов.

Pull-клиенты могут одновременно читать несколько документов в одном потоке.

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

Покажи мне эту магию!

Итак, давайте возьмем набор данных из Википедии в качестве примера, ниже приведен образец данных:

А вот код для извлечения имени и идентификатора каждой страницы:

Простой…

Ждать! Что сейчас произошло?

Вы можете увидеть полный проект здесь, первый фрагмент извлекает XML-файл из zip-архива, хранящегося в папке ресурсов.

В следующей части используется XMLInputFactory для создания StAX читателя из потока и начинается чтение файла. Читатель предоставляет вытягивающий API через поток для извлечения XML-элементов из потока. Этот первый цикл просматривает документ в поисках <page> начального элемента и затем передает управление функции parsePage для обработки элемента страницы.

Функция parsePage продолжает перебирать события в потоке. Сначала мы проверяем, дошли ли мы до конца элемента страницы, и возвращаемся, так как мы прочитали страницу и выполнили объем этой функции.

Затем ищется начальный элемент и предпринимаются действия в зависимости от того, какой из них был достигнут. Это позволяет нам быстро перебирать все элементы, содержащиеся в элементе page, извлекая то, что необходимо, и игнорируя остальное. Дополнительный case можно легко добавить к оператору switch для обработки revision, вызывая дополнительную функцию для обработки деталей синтаксического анализа этого элемента.

Почему это было так просто?

Написание SAXParser похоже на обучение клоуна жонглированию. Это неудобно и делает то, что должно быть относительно простой задачей чтения XML-файла, излишне сложной. Парсер StAX переворачивает это с ног на голову, вместо этого были выброшены элементы без контроля, управление остается за вызывающим. Вместо того, чтобы отдавать контроль и предоставлять функции, которые будут вызываться при возникновении событий. События запрашиваются и обрабатываются по мере необходимости, оставляя контроль за потребителем.

Java - отличный язык, но при правильном использовании это, к сожалению, не всегда так. SAXParser - один из таких примеров, хотя он и служит определенной цели, но является нишевой. Это не должно быть ответом по умолчанию для эффективного синтаксического анализа XML. Я не понимаю, почему это все еще так, когда в стандартной библиотеке существует гораздо лучшее решение.