Методы парсинга 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 всегда превращаться в огромный конечный автомат?

Channel72
источник
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)

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

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

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

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

Александр Гесслер
источник
+1: начать с DOM. Избегайте SAX.
S.Lott
или с vtd-xml
vtd-xml-author
4

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

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

TMN
источник
1

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

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

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

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

Стив
источник
Если вы используете XPath, вы используете DOM (если только вы не используете его с собственным оценщиком XPath).
TMN
да, отсюда мой комментарий об абстракциях, требующих DOM ... но я уточню, спасибо!
Стив
0

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

Деми
источник