Когда мне следует выбирать SAX вместо StAX? - PullRequest
77 голосов
/ 23 сентября 2011

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

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

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

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

Ответы [ 6 ]

79 голосов
/ 23 сентября 2011

Обзор
Документы 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
}

В случае начального элемента 'Building' вам нужно будет определить, что вы на самом деле анализируете Address, а затем направить событие XML методу, задача которого заключается в интерпретации Address.

StAX
StAX «извлекает» события XML , предоставляя вам возможность определить, где в вашей программе / данных получить события XML.

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

Конечно, вы всегда хотели бы получить событие 'Building' в методе, задачей которого является интерпретация Address.

Обсуждение
Разница между 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;
}

Это довольно просто, и здесь нет места для недоразумений. Просто не забудьте правильно уменьшить уровень:

A. после того, как вы ожидали символы, но получили END_ELEMENT в некотором теге, который должен содержать символы (в приведенном выше шаблоне):

<Name>Thomas</Name>

было вместо

<Name></Name>

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

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

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

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

19 голосов
/ 23 сентября 2011

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

РЕДАКТИРОВАТЬ : Согласно этому блогу Java SAX против StAX StAX не предлагают проверки схемы.

15 голосов
/ 06 октября 2011

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

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

1 голос
/ 10 октября 2011

Это все баланс.

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

Я полагаю, что в настоящее время StAX необходимо упаковать через сторонний jar, в то время как SAX поставляется бесплатно в javax.

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

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

0 голосов
/ 19 апреля 2016

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

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

0 голосов
/ 01 апреля 2015

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

Подробнее о StAX можно прочитать в Учебниках по Java StAX

...