Когда следует выбирать SAX над StAX?

Потоковые XML-парсеры, такие как SAX и StAX, быстрее и эффективнее памяти, чем парсеры, строящие древовидную структуру, такую ​​как DOM-парсеры. SAX - это синтаксический анализатор push, означающий, что это экземпляр шаблона наблюдателя (также называемого шаблоном слушателя). SAX был первым, но затем появился StAX - синтаксический анализатор, что означает, что он в основном работает как итератор.

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

В учебнике Java по JAXP StAX смутно представлен как середина между DOM и SAX: "это проще, чем SAX и более эффективно, чем DOM". Тем не менее, я не нашел никаких подсказок, что StAX будет медленнее или меньше памяти, чем SAX.

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

Ответ 1

Чтобы обобщить бит, I think StAX может быть столь же эффективным, как SAX. С улучшенным дизайном StAX I не может найти какой-либо ситуации, когда синтаксический анализ SAX был бы предпочтительным, если только не работать с устаревшим кодом.

EDIT. Согласно этому блогу Java SAX vs. StAX StAX не предлагает проверки схемы.

Ответ 2

Обзор
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 может давать A несколько событий (путем передачи экземпляра 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>

То же самое верно и для недостающего поддерева, вы получаете идею.

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

Обратите внимание, что этот подход полностью игнорирует "невежественные" пробелы, для более надежной реализации.

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

Ответ 3

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

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

Ответ 4

Все это баланс.

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

Я считаю, что в настоящее время StAX нужно упаковывать через стороннюю банку, в то время как SAX поставляется бесплатно в javax.

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

Будущие версии Java почти наверняка будут содержать реализацию StAX, поэтому проблема исчезнет.

Ответ 5

StAX позволяет вам создавать двунаправленные синтаксические анализаторы XML, которые бывают быстрыми. Это доказывает лучшую альтернативу другим методам, таким как DOM и SAX, как с точки зрения производительности, так и с точки зрения удобства использования

Подробнее о StAX вы можете узнать в Java StAX Tutorials

Ответ 6

Большая часть информации, предоставленной этими ответами, несколько устарела... в этом исследовании в 2013 году было проведено всестороннее исследование всех XML-парсинга libs... прочитайте его, и вы легко увидите явного победителя (подсказка: там это только один истинный победитель)...

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