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(...);
lub
void parseAddress(...);
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.
int event = reader.next();
if(event == XMLStreamConstants.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(..) {
XMLStreamReader reader = ....;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
break;
}
} while(reader.hasNext());
MyDataBindingObject object = new MyDataBindingObject();
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
if(reader.getLocalName().equals("Whatever1")) {
WhateverObject child = parseSubTreeForWhatever(reader);
level --;
object.setWhatever(child);
}
if(level == 2) {
parseSubTreeForWhateverAtRelativeLevel2(reader);
level --;
object.setWhatever(child);
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
}
} 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();
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
if(reader.getLocalName().equals("Whatever2")) {
MyWhateverObject child = parseMySubelementTree(reader);
level --;
object.setWhatever(child);
}
if(level == 2) {
MyWhateverObject child = parseMySubelementTree(reader);
level --;
object.setWhatever(child);
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
}
} 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();
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
if(reader.getLocalName().equals("FirstName")) {
String text = reader.getElementText()
if(text.length() > 0) {
myObject.setName(text)
}
level--;
} else if(reader.getLocalName().equals("LastName")) {
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
}
} while(level > 0);
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.