Kiedy powinienem wybrać SAX zamiast StAX?

Strumieniowe parsery xml, takie jak SAX i StAX, są szybsze i wydajniejsze niż parsery budujące strukturę drzewa, taką jak parsery DOM. SAX jest parserem push, co oznacza, że jest instancją wzorca observer (zwanego także listener pattern). SAX był tam pierwszy, ale potem przyszedł StAX-pull parser, co oznacza, że w zasadzie działa jak iterator.

Wszędzie można znaleźć powody, dla których warto preferować StAX od saksofonu, ale zazwyczaj sprowadza się to do: "łatwiej jest używać".

W samouczku Javy na JAXP StAX jest niejasno przedstawiony jako środek między DOM A SAX: "jest łatwiejszy niż SAX i bardziej wydajny niż DOM". Jednak nigdy nie znalazłem żadnych wskazówek, że StAX byłby wolniejszy lub mniej wydajny w pamięci niż SAX.

To wszystko sprawiło, że zastanawiałem się: czy są jakieś powody, aby wybrać saksofon zamiast Staxa?

Author: vtd-xml-author, 2011-09-22

6 answers

Aby trochę uogólnić, myślę, że StAX może być równie skuteczny jak SAX. Dzięki ulepszonemu projektowi StAX nie mogę znaleźć sytuacji, w której preferowane byłoby parsowanie SAX, chyba że praca z kodem starszym.

EDIT : według tego bloga Java SAX vs. StAX StAXoferta nie zawiera walidacji schematu.

 22
Author: Johan Sjöberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-03 12:10:22

Informacje ogólne
dokumenty XML są dokumentami hierarchicznymi, w których te same nazwy elementów i przestrzenie nazw mogą występować w kilku miejscach, o różnym znaczeniu i w nieskończonej głębi (rekurencyjnie). Jak zwykle rozwiązaniem dużych problemów jest podzielenie ich na małe problemy. W kontekście parsowania XML oznacza to parsowanie określonych części XML w metodach specyficznych dla tego XML. Na przykład jedna logika przetworzy adres:

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

Czyli mielibyście metoda

AddressType parseAddress(...); // A

Lub

void parseAddress(...); // B

Gdzieś w Twojej logice, pobranie argumentów wejściowych XML i zwrócenie obiektu (wynik B może być pobrany z pola później).

SAX
sax "wypycha" XML zdarzenia, to Ty decydujesz, gdzie należą zdarzenia XML w twoim programie / danych.

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

W przypadku elementu startowego "Building", musisz określić, czy rzeczywiście parsujesz adres, a następnie przekierować XML Zdarzenie do metody, której zadaniem jest interpretacja adresu.

StAX
StAX "ściąga" zdarzenia XML , decydowanie, gdzie w programie / danych odbierane są zdarzenia XML należy pozostawić Tobie.

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

Oczywiście zawsze chciałbyś otrzymać Zdarzenie 'Building' w metodzie, której zadaniem jest interpretacja adresu.

dyskusja
Różnica między SAX i StAX jest to, że push and pull. W obu przypadkach stan parse musi być jakoś obsługiwany.

To przekłada się na metodę B jako typową dla SAX, a metodę A Dla StAX. Ponadto SAX musi dawać B pojedyncze zdarzenia XML, podczas gdy StAX może dawać wiele zdarzeń (przekazując instancję XMLStreamReader).

Tak więc b najpierw sprawdza poprzedni stan parsowania, a następnie obsługuje każde pojedyncze zdarzenie XML, a następnie zapisuje stan (w polu). Metoda A może obsłużyć wszystkie zdarzenia XML naraz poprzez dostęp do XMLStreamReader wiele razy, aż się usatysfakcjonuje.

wniosek
StAX pozwala na uporządkowanie kodu parsowania (wiązania danych) zgodnie ze strukturą XML; tak więc w odniesieniu do SAX 'a' state ' jest implicite z przepływu programu dla StAX, podczas gdy w SAX, zawsze musisz zachować jakąś zmienną stanu + przekierować przepływ zgodnie z tym stanem, dla większości wywołań zdarzeń.

Polecam StAX dla wszystkich oprócz najprostszych dokumentów. Raczej przenieść się na saksofon jako optymalizacja później (ale prawdopodobnie będziesz chciał przejść binarny do tego czasu).

Postępuj zgodnie z tym wzorcem podczas parsowania za pomocą 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;
}

Więc podmoduł używa mniej więcej tego samego podejścia, tzn. poziomu liczenia:

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;
}

I w końcu osiągniesz poziom, w którym będziesz czytać typy bazowe.

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;
}

Jest to dość proste i nie ma miejsca na nieporozumienia. Pamiętaj tylko o poprawnym obniżaniu poziomu: {]}

A. po oczekiwaniu znaki, ale ma END_ELEMENT w znaczniku, który powinien zawierać znaki (w powyższym wzorze):

<Name>Thomas</Name>

Zamiast

<Name></Name>

To samo dotyczy brakującego podzbioru, rozumiesz.

B. Po wywołaniu metod podczęściowych, które są wywoływane na elementach startowych i zwracane po odpowiednim elemencie końcowym, tzn. parser jest o jeden poziom niższy niż przed wywołaniem metody (powyższy wzorzec).

Zauważ, jak to podejście całkowicie ignoruje "ignorable" spacje również, dla bardziej solidnej implementacji.

parsery
Przejdź z Woodstox dla większości funkcji lub Aaalto-xml dla szybkości.

 81
Author: ThomasRS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-16 12:23:31

@ Rinke: chyba tylko raz myślę o preferowaniu SAX zamiast STAX w przypadku, gdy nie trzeba obsługiwać/przetwarzać treści XML; dla np. jedyną rzeczą, którą chcesz zrobić, to sprawdzić poprawność przychodzącego XML i po prostu chcesz obsłużyć błędy, jeśli to has...in w tym przypadku możesz po prostu wywołać metodę parse() na parserze SAX i określić obsługę błędów do obsługi każdego parsowania problem....so zasadniczo STAX jest zdecydowanie preferowanym wyborem w scenariuszach, w których chcesz obsługiwać zawartość, ponieważ Obsługa treści SAX to zbyt trudne do zakodowania...

Praktycznym przykładem tego przypadku może być, jeśli masz serię węzłów SOAP w systemie korporacyjnym i podstawowy węzeł SOAP pozwala tylko tym SOAP XML przejść przez następny etap, który jest dobrze ukształtowany, to nie widzę powodu, dla którego miałbym używać STAX. Użyłbym tylko saksofonu.

 16
Author: ag112,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-06 07:41:46

To wszystko równowaga.

Możesz zmienić Parser saksofonu w pull Parser za pomocą kolejki blokującej i niektórych sztuczek wątku, więc dla mnie różnica jest znacznie mniejsza niż się wydaje.

Uważam, że obecnie StAX musi być pakowany przez zewnętrzny słoik, podczas gdy SAX jest darmowy w javax.

Ostatnio wybrałem SAX i zbudowałem wokół niego pull parser, więc nie musiałem polegać na zewnętrznym słoiku.

Przyszłe wersje Javy prawie na pewno będą zawierały StAX wdrożenie więc problem znika.

 1
Author: OldCurmudgeon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-09 12:32:08

StAX umożliwia szybkie tworzenie dwukierunkowych parserów XML. Okazuje się lepszą alternatywą dla innych metod, takich jak DOM i SAX, zarówno pod względem wydajności, jak i użyteczności

Możesz przeczytać więcej o StAX w Java StAX Tutorials

 0
Author: Annamalai Thangaraj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-01 09:59:05

Większość informacji zawartych w tych odpowiedziach jest nieco nieaktualna... w tym artykule z 2013 r. przeprowadzono kompleksowe badanie wszystkich bibliotek parsowania XML... przeczytaj, A łatwo zobaczysz wyraźnego zwycięzcę (wskazówka: istnieje tylko jeden prawdziwy zwycięzca)...

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

 -1
Author: vtd-xml-author,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-23 21:08:05