Kiedy powinienem wybrać SAX zamiast StAX?

81

Strumieniowe parsery XML, takie jak SAX i StAX, są szybsze i bardziej wydajne w pamięci niż parsery budujące strukturę drzewa, taką jak parsery DOM. SAX jest parserem wypychania, co oznacza, że ​​jest instancją wzorca obserwatora (nazywanego również wzorcem nasłuchiwania). SAX był tam pierwszy, ale potem pojawił się StAX - parser ściągający, co oznacza, że ​​zasadniczo działa jak iterator.

Możesz znaleźć powody, dla których wolisz StAX zamiast SAX wszędzie, ale zwykle sprowadza się to do: „jest łatwiejszy w użyciu”.

W samouczku Java dotyczącym JAXP StAX jest niejasno przedstawiany jako środek pomiędzy DOM i SAX: „jest łatwiejszy niż SAX i wydajniejszy niż DOM”. Jednak nigdy nie znalazłem żadnych wskazówek, że StAX byłby wolniejszy lub mniej wydajny w pamięci niż SAX.

Wszystko to sprawiło, że zacząłem się zastanawiać: czy są jakieś powody, aby wybrać SAX zamiast StAX?

Rinke
źródło

Odpowiedzi:

22

Aby trochę uogólnić, myślę, że StAXmoże być tak samo skuteczny jak SAX. Dzięki ulepszonemu projektowi StAXnie mogę znaleźć żadnej sytuacji, w której SAXparsowanie byłoby preferowane, chyba że pracowałem ze starszym kodem.

EDYCJA : Według tego bloga Java SAX vs. StAX StAX nie oferują żadnej walidacji schematu.

Johan Sjöberg
źródło
2
nie jest trudno dodać walidację do stax. wdrożyłem to sobie któregoś dnia.
jtahlborn
Więcej szczegółów na temat walidacji: stackoverflow.com/questions/5793087/stax-xml-validation
Ben
81

Przegląd
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 o nieskończonej głębokości (rekurencyjnie). Jak zwykle, rozwiązaniem dużych problemów jest podzielenie ich na małe problemy. W kontekście analizowania XML oznacza to analizowanie określonych części XML w metodach specyficznych dla tego XML. Na przykład jeden element logiki przeanalizowałby adres:

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

tj. miałbyś metodę

AddressType parseAddress(...); // A

lub

void parseAddress(...); // B

gdzieś w swojej logice, pobierając argumenty wejściowe XML i zwracając obiekt (wynik B można później pobrać z pola).

SAX
SAX „wypycha” zdarzenia XML , pozostawiając Tobie określenie, gdzie zdarzenia XML należą do twojego programu / 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 początkowego „Budowanie” należy określić, że faktycznie analizujemy adres, a następnie skierować zdarzenie XML do metody, której zadaniem jest interpretacja adresu.

StAX
StAX „ściąga” zdarzenia XML , pozostawiając użytkownikowi określenie, gdzie w programie / danych mają być odbierane zdarzenia XML.

// 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 „Budynek” w metodzie, której zadaniem jest interpretacja adresu.

Dyskusja
Różnica między SAX i StAX polega na push and pull. W obu przypadkach stan analizy musi być jakoś obsłużony.

Przekłada się to na metodę B typową dla SAX i metodę A dla StAX. Ponadto SAX musi dać B indywidualne zdarzenia XML, podczas gdy StAX może dać A wiele zdarzeń (przez przekazanie instancji XMLStreamReader).

W ten sposób B najpierw sprawdza poprzedni stan analizy, a następnie obsługuje poszczególne zdarzenia XML, a następnie zapisuje stan (w polu). Metoda A może po prostu obsłużyć wszystkie zdarzenia XML naraz, uzyskując dostęp do XMLStreamReader wiele razy, aż będzie spełniony.

Podsumowanie
StAX pozwala ustrukturyzować kod parsowania (powiązanie danych) zgodnie ze strukturą XML ; więc w odniesieniu do SAX, „stan” jest domniemany z przebiegu programu dla StAX, podczas gdy w SAX zawsze trzeba zachować jakąś zmienną stanu + skierować przepływ zgodnie z tym stanem dla większości wywołań zdarzeń.

Polecam StAX do wszystkich dokumentów oprócz najprostszych. Zamiast tego przejdź do SAX jako optymalizacji później (ale do tego czasu prawdopodobnie będziesz chciał przejść do trybu binarnego).

Postępuj zgodnie z tym wzorcem podczas analizowania przy użyciu 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;
}

Zatem podmetoda wykorzystuje mniej więcej to samo podejście, tj. Poziom zliczania:

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, na którym będziesz czytać typy podstawowe.

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 prawidłowym zmniejszaniu poziomu:

A. po oczekiwaniu znaków, ale w jakimś tagu otrzymałeś END_ELEMENT, który powinien zawierać znaki (w powyższym wzorcu):

<Name>Thomas</Name>

był zamiast

<Name></Name>

To samo dotyczy brakującego poddrzewa, rozumiesz.

B. po wywołaniu metod subparsingu, które są wywoływane na elementach startowych i zwracają PO odpowiednim elemencie końcowym, czyli parser jest o jeden poziom niższy niż przed wywołaniem metody (powyższy wzorzec).

Zwróć uwagę, że to podejście całkowicie ignoruje również „ignorowalne” białe znaki, aby zapewnić bardziej niezawodną implementację.

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

ThomasRS
źródło
W oświadczeniu otwierającym brzmi „… podczas gdy w SAX…”. Czy to pomyłka? („SAX” zamiast „StAX”) W każdym razie dziękuję za odpowiedź. Jeśli dobrze cię rozumiem, mówisz, że stan niejawny w podejściu SAX jest zaletą w porównaniu z potrzebą śledzenia lokalizacji drzewa xml w podejściu StAX.
Rinke
Dzięki za (teraz jeszcze bardziej rozbudowaną) odpowiedź. Obawiam się, że nadal nie widzę, jaki byłby dobry powód, aby użyć SAX zamiast StAX. Twoja odpowiedź to dobre wyjaśnienie, jak działają oba procesory.
Rinke
W przypadku prostych dokumentów są takie same. Spójrz na przykład na ten schemat: mpeg.chiariglione.org/technologies/mpeg-21/mp21-did/index.htm i StAX będą bardziej praktyczne.
ThomasRS
Krótko mówiąc, skoro już piszesz swój kod, wiesz, która część dokumentu analizujesz, tj. Cała logika mapowania zdarzenia SAX do poprawnego kodu, jest marnowana.
ThomasRS
16

@Rinke: Myślę, że tylko raz myślę o preferowaniu SAX zamiast STAX w przypadku, gdy nie musisz obsługiwać / przetwarzać zawartości XML; na przykład jedyne, co chcesz zrobić, to sprawdzić poprawność przychodzącego XML i po prostu chcesz obsłużyć błędy, jeśli ma ... 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 dowolnego problem z analizowaniem ... więc zasadniczo STAX jest zdecydowanie lepszym wyborem w scenariuszach, w których chcesz obsługiwać zawartość, ponieważ program obsługi treści SAX jest zbyt trudny do zakodowania ...

Jednym praktycznym przykładem tego przypadku może być sytuacja, w której w systemie przedsiębiorstwa znajduje się szereg węzłów SOAP, a węzeł SOAP poziomu podstawowego pozwala przejść przez następny etap tylko tym kodom XML protokołu SOAP, które są poprawnie sformułowane, to nie widzę żadnego powodu, dla którego ja użyłby STAX. Po prostu użyłbym SAX.

ag112
źródło
Jak dotąd wybrałem tę odpowiedź jako najlepszą. Chociaż jest to dobra odpowiedź, nie uważam, że jest w 100% autorytatywna i jasna. Mile widziane są nowe odpowiedzi.
Rinke
1

Wszystko jest równowagą.

Możesz zmienić parser SAX w parser ściągający, używając kolejki blokującej i pewnych sztuczek wątków, więc według mnie różnica jest znacznie mniejsza niż na początku.

Uważam, że obecnie StAX musi być zapakowany za pośrednictwem zewnętrznego słoika, podczas gdy SAX jest dostępny bezpłatnie w javax.

Niedawno wybrałem SAX i zbudowałem wokół niego parser ściągania, więc nie musiałem polegać na jaru innej firmy.

Przyszłe wersje Javy prawie na pewno będą zawierały implementację StAX, więc problem zniknie.

OldCurmudgeon
źródło
1
Java SE 6 zawiera StAX. Ale np. Implementacja Androida tego nie obejmuje.
Bjarne Boström
0

StAX umożliwia tworzenie dwukierunkowych parserów XML, które są szybkie. 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 samouczkach Java StAX

Annamalai Thangaraj
źródło
-1

Większość informacji zawartych w tych odpowiedziach jest nieco nieaktualna ... w tym artykule badawczym z 2013 roku przeprowadzono kompleksowe badanie wszystkich bibliotek analizujących XML ... przeczytaj je, a z łatwością zobaczysz wyraźnego zwycięzcę (wskazówka: jest tylko jeden prawdziwy zwycięzca) ...

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

vtd-xml-author
źródło
1
Przeczytałem artykuł, zwycięzcą jest StAX używający API kursora jak w XMLStreamReader.
Roland
bardzo zabawne :), masz na myśli zwycięzcę wyścigu żółwi :)
vtd-xml-author
Właśnie ponownie przeczytałem artykuł i tak, StaX jest lepszy od vtd, szybszy i mniejszy zużycie pamięci. O co ci chodzi?
Roland
zwycięzcą jest stAX w jaki sposób? do której części artykułu się odnosisz? modyfikowanie dokumentu czy wybieranie czy różnicowanie? najwyraźniej autor artykułu doszedł do innego wniosku. ale mogą się całkowicie mylić ...
vtd-xml-author
1
np. strona 80: Zgodnie z wynikami (rysunek 11 i rysunek 12) widzimy, że StAX jest API, które ma lepszą wydajność, a następnie VTD. Jednak VTD zużywa znaczną ilość pamięci. Zużycie pamięci może być wąskim gardłem dla środowisk, które zapewniają ograniczone możliwości.
Roland