Методики розбору 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 до xml aka XLinq.
Muad'Dib

Дякую, я думав, що я єдиний із цією проблемою. Чесно кажучи, я часто вважаю, що весь формат XML є скоріше перешкодою, ніж допомогою. Так, це дозволяє зберігати безліч структурованих даних у невеликому текстовому файлі. Але якщо вам знадобиться 20+ класів, щоб розпакувати і зрозуміти річ - без жодної гарантії, що ви не будете випускати щось більш-менш важливе. Це як зайчик у Святому Граалі Монті Пітона.
Elise van Looij

Відповіді:


9

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

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

Приклад, який ви даєте, виглядає для мене дуже DOM.

  1. Завантажте XML
  2. Витягніть титульний вузол (и) та "зробіть щось із ними".

EDIT: Я б також використовував SAX для потоків, які можуть бути неправильно сформовані, але там, де я хочу зробити найкращу здогадку при виведенні даних.


2
Я думаю, що це хороший момент. Якщо ви розбираєте документи, які занадто великі для DOM, вам потрібно розглянути, чи ви розбираєте документи, які занадто великі для XML
Дін Хардінг

1
+1: Враховуючи такий варіант, я завжди ходив би з DOM. На жаль, схоже, що наші вимоги до дизайну завжди включають "вміння обробляти будь-який розмірний документ" та "повинен бути виконаним", що в значній мірі виключає рішення на основі DOM.
TMN

3
@TMN, в ідеальному світі, що вимоги в першу чергу виключають XML.
SK-логіка

1
@TMN, це звучить як одна з цих фантомних вимог: "Звичайно, всі наші документи лише близько 100 КБ, і найбільший, який ми бачили, - 1 Мб, але ви ніколи не знаєте, що має бути в майбутньому, тому ми повинні тримати наші параметри відкритими і будуйте для нескінченно великих документів "
Пол М'ясник

@Paul Butcher, ти ніколи не знаєш. Я маю на увазі, звалище Вікіпедії - це як 30 ГБ XML.
Канал72

7

Я не дуже працюю з XML, трохи, на мою думку, мабуть, одним із найкращих способів розбору XML з бібліотекою є використання XPath.

Замість того, щоб об'їхати дерево, щоб знайти якийсь конкретний вузол, ви даєте шлях до нього. У випадку вашого прикладу (у псевдокоді) це було б щось на зразок:

books = parent.xpath ("/ book") // Це дасть вам усі книжкові вузли
за-кожну книгу в книгах
    title = book.xpath ("/ назва / текст ()")
    author = book.xpath ("/ автор / текст ()")
    ціна = book.xpath ("/ ціна / текст ()")

    // Робіть справи з даними

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


Це добре, якщо ви можете заздалегідь знати та довіряти тому, як структурується XML. Якщо ви не знаєте, чи, скажімо, ширина елемента буде вказана як атрибут вузла або як вузол атрибута всередині вузла розміру елемента, то XPath не допоможе.
Elise van Looij

5

Потрібно завжди на основі потоку XML-розбору завжди перетворюватися на величезну машину?

Зазвичай так і є.

Для мене слід зазначити використання повноцінного аналізатора DOM - це тоді, коли мені потрібно буде імітувати частини пам’яті ієрархії файлів у пам’яті, наприклад, щоб мати змогу вирішувати перехресні посилання в документі.


+1: Почніть з DOM. Уникайте SAX.
S.Lott

або з vtd-xml
vtd-xml-author

4

Парсинг загалом - це просто керування державною машиною, і розбір XML нічим не відрізняється. Потоковий розбір - це завжди клопот, я завжди закінчую створення якогось стека, щоб відстежувати вузли предків, визначаючи безліч подій та певного диспетчера подій, який перевіряє тег чи реєстр подій і запускає подію якщо одна відповідає. Основний код досить тугий, але я закінчую величезною купою обробників подій, які здебільшого полягають у призначенні значення наступного текстового вузла полі в структурі десь. Це може стати досить волохатим, якщо вам теж потрібно змішати ділову логіку.

Я б завжди використовував DOM, якщо проблеми чи розміру чи продуктивності не диктували інше.


1

Не повністю мовний агностик, але я зазвичай десеріалізую XML в об'єкти, а не навіть думаю про розбір. Єдиний час, щоб потурбуватися про розбір стратегій як такий, якщо у вас є проблеми зі швидкістю.


Це підпадає під розбір. Якщо XML, про який йде мова, не є результатом серіалізації об'єктів і у вас є готова бібліотека десеріалізації. Але тоді це питання не з’являється.

Багато мов / стеків мають готові бібліотеки десеріалізації.
Wyatt Barnett

Так, так що? Мої пункти по- , як і раніше тримати - не всі XML - файли в дикій природі приходять в такому форматі, і якщо у вас є той , який робить, ви не поставити це питання , як ви просто використовувати цю бібліотеку десеріалізациі і не розбір нічого на свій розсуд, з потоків чи іншим способом.

0

Це стає набагато менш громіздким, якщо ви можете використовувати XPath. І в .Net land LINQ до XML також абстрагує багато менш гламурних речей. ( Редагувати - для цього потрібен курс DOM)

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


Якщо ви використовуєте XPath, ви використовуєте DOM (якщо ви не використовуєте його з домашнім оцінювачем XPath).
TMN

так, звідси мій коментар щодо абстракцій, які вимагають DOM ... але я уточню, дякую!
Стів

0

Якщо ви можете знайти аналізатор, який дає вам ітератор, чи думали ви розглянути його як лексеру та скористатися генератором державної машини?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.