Методы парсинга XML


11

Я всегда считал, что XML довольно сложно обрабатывать. Я не говорю о реализации парсера XML: я говорю об использовании существующего потокового парсера, такого как парсер SAX, который обрабатывает XML узел за узлом.

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

Например, учитывая фрагмент из типичного XML-документа:

<book>
  <title>Blah blah</title>
  <author>Blah blah</author>
  <price>15 USD</price>
</book>

... Как определить, когда я столкнулся с текстовым узлом, содержащим название книги? Предположим, у нас есть простой анализатор XML, который действует как итератор, давая нам следующий узел в документе XML каждый раз, когда мы вызываем XMLParser.getNextNode(). Я неизбежно обнаруживаю, что пишу такой код:

boolean insideBookNode = false;
boolean insideTitleNode = false;

while (!XMLParser.finished())
{
    ....
    XMLNode n = XMLParser.getNextNode();

    if (n.type() == XMLTextNode)
    {
        if (insideBookNode && insideTitleNode)
        {
            // We have a book title, so do something with it
        }
    }
    else
    {
        if (n.type() == XMLStartTag)
        {
            if (n.name().equals("book")) insideBookNode = true
            else if (n.name().equals("title")) insideTitleNode = true;
        }
        else if (n.type() == XMLEndTag)
        {
            if (n.name().equals("book")) insideBookNode = false;
            else if (n.name().equals("title")) insideTitleNode = false;
        }
    }
}

По сути, обработка XML быстро превращается в огромный цикл, управляемый конечным автоматом, с большим количеством переменных состояния, используемых для обозначения родительских узлов, которые мы нашли ранее. В противном случае необходимо сохранить объект стека для отслеживания всех вложенных тегов. Это быстро становится подверженным ошибкам и трудным в обслуживании.

Опять же, проблема заключается в том, что интересующие нас данные не связаны напрямую с отдельным узлом. Конечно, это может быть, если мы напишем XML как:

<book title="Blah blah" author="blah blah" price="15 USD" />

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

Итак ... я делаю что-то не так? Есть ли способ лучше? В какой момент использование парсера на основе XML-потока становится слишком громоздким, так что становится необходимым полноценный парсер DOM? Я хотел бы услышать от других программистов, какие идиомы они используют при обработке XML с помощью потоковых парсеров. Должен ли потоковый анализ XML всегда превращаться в огромный конечный автомат?


2
если вы используете язык .net, вы должны взглянуть на linq to xml или XLinq.
— Muad'Dib

Спасибо, я думал, что я был единственным с этой проблемой. Честно говоря, я часто нахожу весь формат XML скорее препятствием, чем помощью. Да, это позволяет хранить много структурированных данных в небольшом текстовом файле. Но если вам нужно более 20 классов, чтобы распаковать вещи и разобраться в этом - без гарантии, что вы не пропустите что-то более или менее важное. Это как кролик в Святом Граале Монти Пайтона.
— Элиз ван Лоидж

Ответы:


9

Для меня вопрос наоборот. В какой момент XML-документ становится настолько громоздким, что вам приходится использовать SAX вместо DOM?

Я бы использовал SAX только для очень большого потока данных неопределенного размера; или если поведение, которое должен вызывать XML, действительно управляемо событиями, и, следовательно, SAX-подобным.

Пример, который вы приводите, выглядит для меня очень похожим на DOM.

  1. Загрузите XML
  2. Извлеките заголовочные узлы и «сделайте что-нибудь с ними».

РЕДАКТИРОВАТЬ: Я бы также использовал SAX для потоков, которые могут быть искажены, но там, где я хочу сделать наилучшее предположение при получении данных.


2
Я думаю, что это хороший момент. Если вы анализируете документы, которые слишком велики для DOM, вам нужно подумать, анализируете ли вы документы, которые слишком велики для XML
— Дин Хардинг,

1
+1: учитывая опцию, я всегда буду идти с DOM. К сожалению, кажется, что наши требования к дизайну всегда включают «способность обрабатывать документ любого размера» и «должны быть эффективными», что в значительной степени исключает решения на основе DOM.
— TMN

3
@TMN, в идеальном мире эти требования исключают XML в первую очередь.
— SK-logic

1
@ TMN, это звучит как одно из этих фантомных требований: «Конечно, все наши документы имеют размер около 100 КБ, и самое большое, что мы видели, это 1 МБ, но вы никогда не знаете, что нас ждет в будущем, поэтому мы должны держать наши варианты открытыми. и строить для бесконечно больших документов "
— Пол Бучер

@ Пол Мясник, ты никогда не знаешь. Я имею в виду, что дамп Википедии похож на 30 ГБ XML.
— Channel72

7

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

Вместо того, чтобы обходить дерево, чтобы найти какой-то конкретный узел, вы указываете путь к нему. В случае вашего примера (в псевдокоде) это будет что-то вроде:

books = parent.xpath ("/ book") // Это даст вам все узлы книги
за каждую книгу в книгах
    title = book.xpath ("/ title / text ()")
    author = book.xpath ("/ author / text ()")
    цена = book.xpath ("/ price / text ()")

    // Делаем вещи с данными

XPath намного мощнее, вы можете выполнять поиск с использованием условий (как по значениям, так и по атрибутам), выбирать определенный узел в списке, перемещать уровни по дереву. Я рекомендую вам поискать информацию о том, как его использовать, он реализован во многих библиотеках синтаксического анализа (я использую его. Версия .Net Framework и lxml для Python)


Это хорошо, если вы можете заранее знать и доверять структуре XML. Если вы не знаете, будет ли, скажем, ширина элемента указана как атрибут узла или как узел атрибута внутри узла размера элемента, то XPath не сильно поможет.
— Элиз ван Лоидж

5

Должен ли потоковый анализ XML всегда превращаться в огромный конечный автомат?

Обычно так и есть, да.

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


+1: начать с DOM. Избегайте SAX.
— S.Lott

или с vtd-xml
— vtd-xml-author

4

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

Я бы всегда использовал DOM, если только размер или производительность не определялись иначе.


1

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


Это подпадает под разбор. Если рассматриваемый XML не является результатом сериализации объекта, и у вас есть готовая библиотека десериализации. Но тогда этот вопрос не появляется.

Многие языки / стеки имеют готовые библиотеки десериализации.
— Уайетт Барнетт

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

0

Это станет намного менее громоздким, если вы можете использовать XPath. И в .Net land LINQ to XML также абстрагирует множество менее гламурных вещей. ( Изменить - это, конечно, требует подхода DOM)

По сути, если вы используете потоковый подход (поэтому вы не можете использовать более хорошие абстракции, требующие DOM), я думаю, что это всегда будет довольно громоздким, и я не уверен, что есть какой-то способ обойти это.


Если вы используете XPath, вы используете DOM (если только вы не используете его с собственным оценщиком XPath).
— TMN

да, отсюда мой комментарий об абстракциях, требующих DOM ... но я уточню, спасибо!
— Стив

0

Если вы можете найти парсер, который дает вам итератор, задумывались ли вы о том, чтобы рассматривать его как лексер и использовать генератор конечных автоматов?

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.