Коли я повинен вибрати SAX замість StAX?


81

Трансляція xml-синтаксичних аналізаторів, таких як SAX та StAX, є швидшою та ефективнішою, ніж парсери, що створюють деревоподібну структуру, як DOM-парсери. SAX - синтаксичний синтаксичний аналізатор, що означає, що він є екземпляром шаблону спостерігача (також званий шаблоном слухача). Спочатку був SAX, але потім з’явився StAX - синтаксичний аналізатор, що означає, що він в основному працює як ітератор.

Ви можете знайти причини, чому віддавати перевагу StAX перед SAX, скрізь, але зазвичай це зводиться до: "його простіше використовувати".

У навчальному посібнику з Java про JAXP StAX неясно представлений як посередині між DOM і SAX: "це простіше, ніж SAX, і ефективніше, ніж DOM". Однак я ніколи не знайшов підказки про те, що StAX буде повільнішим або менш ефективним для пам'яті, ніж SAX.

Все це змусило мене задуматися: чи є причини вибрати SAX замість StAX?

Відповіді:


22

Щоб узагальнити трохи, я думаю, StAXможе бути настільки ж ефективним, як SAX. Завдяки вдосконаленому дизайну StAXя дійсно не можу знайти жодної ситуації, коли SAXаналіз був би кращим, якщо не працювати зі застарілим кодом.

EDIT : Відповідно до цього блогу Java SAX проти StAX StAX не пропонують перевірку схем.


2
не надто складно додати перевірку поверх stax. реалізував це сам днями.
jtahlborn

Детальніше про перевірку: stackoverflow.com/questions/5793087/stax-xml-validation
Бен,

81

Оглядові
документи XML - це ієрархічні документи, де однакові імена елементів та простори імен можуть траплятися в кількох місцях, що мають різне значення, та в інфінітивній глибині (рекурсивно). Як правило, вирішення великих проблем полягає в тому, щоб розділити їх на маленькі проблеми. У контексті аналізу XML це означає аналіз певних частин XML у методах, специфічних для цього XML. Наприклад, одна частина логіки розбере адресу:

<Address>
    <Street>Odins vei</Street>    
    <Building>4</Building>
    <Door>b</Door>
</Address>

тобто у вас був би метод

AddressType parseAddress(...); // A

або

void parseAddress(...); // B

десь у вашій логіці, беручи XML вводить аргументи і повертаючи об'єкт (результат B можна отримати пізніше з поля).

SAX
SAX "штовхає" XML- події , залишаючи за вами визначати, де XML-події належать вашій програмі / даним.

// method in stock SAX handler
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
    // .. your logic here for start element
}

У випадку початкового елемента "Побудова" вам потрібно буде визначити, що ви насправді аналізуєте адресу, а потім направити подію XML до методу, завданням якого є інтерпретація адреси.

StAX
StAX "витягує" XML- події , залишаючи за вами самостійно визначати, де у вашій програмі / даних приймати XML-події.

// method in standard StAX reader
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
    // .. your logic here for start element
}

Звичайно, ви завжди хочете отримати подію "Побудова" в методі, завданням якого є інтерпретація адреси.

Обговорення
Різниця між SAX та StAX полягає в тому, що натискати та тягнути. В обох випадках із станом синтаксичного аналізу потрібно якось обробляти.

Це означає метод B як типовий для SAX та метод A для StAX. Крім того, SAX повинен подавати B окремих подій XML, тоді як StAX може подавати кілька подій (передаючи екземпляр XMLStreamReader).

Таким чином, B спочатку перевіряє попередній стан розбору, а потім обробляє кожну окрему подію XML, а потім зберігає стан (у полі). Метод A може просто обробляти події XML одночасно, отримуючи доступ до XMLStreamReader кілька разів, доки це не буде виконано.

Висновок
StAX дозволяє структурувати код синтаксичного аналізу (прив'язки даних) відповідно до структури XML ; тому щодо SAX, "стан" є неявним із потоку програми для StAX, тоді як у SAX для більшості викликів подій завжди потрібно зберігати якусь змінну стану + маршрутизувати потік відповідно до цього стану.

Я рекомендую StAX для всіх, крім найпростіших документів. Швидше перейдіть до SAX як оптимізація пізніше (але ви, мабуть, захочете перейти в бінарний файл до того часу).

Дотримуйтесь цього шаблону при розборі за допомогою StAX:

public MyDataBindingObject parse(..) { // provide input stream, reader, etc

        // set up parser
        // read the root tag to get to level 1
        XMLStreamReader reader = ....;

        do {
            int event = reader.next();
            if(event == XMLStreamConstants.START_ELEMENT) {
              // check if correct root tag
              break;
            }

            // add check for document end if you want to

        } while(reader.hasNext());

        MyDataBindingObject object = new MyDataBindingObject();
        // read root attributes if any

        int level = 1; // we are at level 1, since we have read the document header

        do {
            int event = reader.next();
            if(event == XMLStreamConstants.START_ELEMENT) {
                level++;
                // do stateful stuff here

                // for child logic:
                if(reader.getLocalName().equals("Whatever1")) {
                    WhateverObject child = parseSubTreeForWhatever(reader);
                    level --; // read from level 1 to 0 in submethod.

                    // do something with the result of subtree
                    object.setWhatever(child);
                }

                // alternatively, faster
                if(level == 2) {
                    parseSubTreeForWhateverAtRelativeLevel2(reader);
                    level --; // read from level 1 to 0 in submethod.

                    // do something with the result of subtree
                    object.setWhatever(child);
                }


            } else if(event == XMLStreamConstants.END_ELEMENT) {
                level--;
                // do stateful stuff here, too
            }

        } while(level > 0);

        return object;
}

Отже, підметод використовує приблизно той самий підхід, тобто рівень підрахунку:

private MySubTreeObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {

    MySubTreeObject object = new MySubTreeObject();
    // read element attributes if any

    int level = 1;
    do {
        int event = reader.next();
        if(event == XMLStreamConstants.START_ELEMENT) {
            level++;
            // do stateful stuff here

            // for child logic:
            if(reader.getLocalName().equals("Whatever2")) {
                MyWhateverObject child = parseMySubelementTree(reader);
                level --; // read from level 1 to 0 in submethod.

                // use subtree object somehow
                object.setWhatever(child);
            }

            // alternatively, faster, but less strict
            if(level == 2) {
              MyWhateverObject child = parseMySubelementTree(reader);
                level --; // read from level 1 to 0 in submethod.

                // use subtree object somehow
                object.setWhatever(child);
            }


        } else if(event == XMLStreamConstants.END_ELEMENT) {
            level--;
            // do stateful stuff here, too
        }

    } while(level > 0);

    return object;
}

І тоді зрештою ви досягнете рівня, на якому ви будете читати базові типи.

private MySetterGetterObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {

    MySetterGetterObject myObject = new MySetterGetterObject();
    // read element attributes if any

    int level = 1;
    do {
        int event = reader.next();
        if(event == XMLStreamConstants.START_ELEMENT) {
            level++;

            // assume <FirstName>Thomas</FirstName>:
            if(reader.getLocalName().equals("FirstName")) {
               // read tag contents
               String text = reader.getElementText()
               if(text.length() > 0) {
                    myObject.setName(text)
               }
               level--;

            } else if(reader.getLocalName().equals("LastName")) {
               // etc ..
            } 


        } else if(event == XMLStreamConstants.END_ELEMENT) {
            level--;
            // do stateful stuff here, too
        }

    } while(level > 0);

    // verify that all required fields in myObject are present

    return myObject;
}

Це досить просто, і тут немає місця для непорозумінь. Тільки пам’ятайте, щоб правильно зменшити рівень:

А. після того, як ви очікували символів, але отримали END_ELEMENT в якомусь тезі, який повинен містити символи (за наведеним вище зразком):

<Name>Thomas</Name>

був замість цього

<Name></Name>

Те саме стосується і відсутнього піддерева, ви розумієте.

Б. після виклику методів субпарсингу, які викликаються на початкових елементах, і повертається ПІСЛЯ відповідного кінцевого елемента, тобто синтаксичний аналізатор знаходиться на один рівень нижче, ніж до виклику методу (наведений вище шаблон).

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

Парсери
Go з Woodstox для більшості функцій або Aaalto-XML для швидкості.


У вашому вступному слові написано "... тоді як у SAX ...". Це друкарська помилка? ("SAX" замість "StAX") У будь-якому випадку дякую за відповідь. Якщо я вас правильно розумію, ви говорите, що неявний стан у підході SAX є перевагою порівняно з необхідністю відстеження вашого розташування дерева xml у підході StAX.
Рінке

Дякую за (тепер ще більш розгорнуту) відповідь. Боюсь, я досі не бачу, що було б вагомою причиною використання SAX замість StAX. Ваша відповідь - гарне пояснення того, як працюють обидва процесори.
Рінке,

Для простих документів вони однакові. Подивіться, наприклад, на цю схему: mpeg.chiariglione.org/technologies/mpeg-21/mp21-did/index.htm, а StAX буде більш практичною.
ThomasRS,

У двох словах, оскільки ви вже пишете свій код, ви розумієте, яку частину документа ви аналізуєте, тобто вся логіка для відображення події SAX є правильним кодом.
ThomasRS

16

@Rinke: Я думаю, лише раз я думаю, що віддаю перевагу SAX перед STAX на випадок, коли вам не потрібно обробляти / обробляти вміст XML; наприклад, єдине, що ви хочете зробити, це перевірити правильність форми вхідного XML і просто хочете обробляти помилки, якщо вони є ... у цьому випадку ви можете просто викликати метод parse () на синтаксичному аналізаторі SAX і вказати обробник помилок для обробки будь-якого проблема аналізу .... тому в основному STAX, безумовно, кращий вибір у сценаріях, коли ви хочете обробляти вміст, оскільки обробник вмісту SAX занадто складний для кодування ...

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


Я вибрав цю відповідь як найкращу на даний момент. Хоча це хороша відповідь, я не вважаю, що це на 100% авторитетне і зрозуміле. Вітаються нові відповіді.
Рінке,

1

Це все баланс.

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

Я вважаю, що в даний час StAX потрібно упаковувати через сторонні банки, тоді як SAX надається безкоштовно в javax.

Нещодавно я вибрав SAX і створив навколо нього синтаксичний аналізатор, тому мені не потрібно було покладатися на сторонній jar.

Майбутні версії Java майже напевно містять реалізацію StAX, тому проблема зникає.


1
Java SE 6 включає StAX. Але, наприклад, реалізація Android не включає його.
Bjarne Boström

0

StAX дозволяє створювати двонаправлені синтаксичні аналізатори XML. Це виявляється кращою альтернативою іншим методам, таким як DOM і SAX, як з точки зору продуктивності, так і зручності використання

Ви можете прочитати більше про StAX у підручниках з Java StAX


-1

Більшість інформації, наданої цими відповідями, дещо застаріли ... у цій дослідницькій роботі 2013 року було проведено всебічне вивчення всіх синтаксичних аналізів XML ... прочитайте її, і ви легко побачите явного переможця (підказка: є лише один справжній переможець) ...

http://recipp.ipp.pt/bitstream/10400.22/1847/1/ART_BrunoOliveira_2013.pdf


1
Я читав газету, переможець StAX з допомогою курсору API , як і в XMLStreamReader.
Роланд

дуже смішно :), ви маєте на увазі переможця гонки черепах :)
vtd-xml-автор

Я просто перечитав папір, і так, StaX перевершує vtd, швидше та менше споживання пам'яті. Тож у чому Ваша думка?
Роланд

переможцем є stAX яким способом? яку частину статті ви маєте на увазі? модифікація документа або вибір чи диференціація? очевидно, автор статті зробив інший висновок. але вони можуть бути абсолютно неправильними ...
vtd-xml-author

1
наприклад, сторінка 80: Згідно з результатами (рисунок 11 і малюнок 12) ми можемо побачити, що StAX - це API, який має кращу продуктивність, за яким слідує VTD. Однак VTD споживає значну кількість пам'яті. Споживання пам'яті може бути вузьким місцем для середовищ, що забезпечують обмежені можливості.
Роланд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.