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?
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.
źródło
@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.
źródło
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.
źródło
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
źródło
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
źródło
XMLStreamReader
.