Übersicht
XML-Dokumente sind hierarchische Dokumente, bei denen dieselben Elementnamen und Namespaces an mehreren Stellen mit unterschiedlicher Bedeutung und in unendlicher Tiefe (rekursiv) vorkommen können. Wie üblich besteht die Lösung für große Probleme darin, sie in kleine Probleme zu unterteilen. Im Zusammenhang mit der XML-Analyse bedeutet dies, dass bestimmte Teile von XML in für diese XML spezifischen Methoden analysiert werden. Zum Beispiel würde eine Logik eine Adresse analysieren:
<Address>
<Street>Odins vei</Street>
<Building>4</Building>
<Door>b</Door>
</Address>
dh Sie hätten eine Methode
AddressType parseAddress(...);
oder
void parseAddress(...);
Irgendwo in Ihrer Logik können Sie XML-Eingabeargumente verwenden und ein Objekt zurückgeben (das Ergebnis von B kann später aus einem Feld abgerufen werden).
SAX
SAX 'pusht' XML- Ereignisse und überlässt es Ihnen zu bestimmen, wo die XML-Ereignisse in Ihr Programm / Ihre Daten gehören.
// method in stock SAX handler
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
// .. your logic here for start element
}
Im Fall eines Startelements 'Building' müssen Sie feststellen, dass Sie tatsächlich eine Adresse analysieren, und dann das XML-Ereignis an die Methode weiterleiten, deren Aufgabe es ist, die Adresse zu interpretieren.
StAX
StAX 'zieht' XML- Ereignisse ab und überlässt es Ihnen zu bestimmen, wo in Ihrem Programm / Ihren Daten die XML-Ereignisse empfangen werden sollen.
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
}
Natürlich möchten Sie immer ein 'Building'-Ereignis in der Methode erhalten, deren Aufgabe es ist, die Adresse zu interpretieren.
Diskussion
Der Unterschied zwischen SAX und StAX besteht im Drücken und Ziehen. In beiden Fällen muss der Analysezustand irgendwie behandelt werden.
Dies führt zu Methode B als typisch für SAX und Methode A als StAX. Darüber hinaus muss SAX B einzelne XML-Ereignisse zuweisen, während StAX A mehrere Ereignisse zuweisen kann (durch Übergeben einer XMLStreamReader-Instanz).
Daher überprüft B zuerst den vorherigen Status der Analyse und behandelt dann jedes einzelne XML-Ereignis und speichert dann den Status (in einem Feld). Methode A kann nur die XML-Ereignisse auf einmal verarbeiten, indem mehrmals auf den XMLStreamReader zugegriffen wird, bis er erfüllt ist.
Fazit Mit
StAX können Sie Ihren Parsing-Code (Datenbindungscode) gemäß der XML-Struktur strukturieren . In Bezug auf SAX ist der 'Status' aus dem Programmablauf für StAX implizit, während Sie in SAX für die meisten Ereignisaufrufe immer eine Art Statusvariable beibehalten und den Fluss gemäß diesem Status weiterleiten müssen.
Ich empfehle StAX für alle außer den einfachsten Dokumenten. Wechseln Sie später lieber als Optimierung zu SAX (aber bis dahin möchten Sie wahrscheinlich binär werden).
Befolgen Sie dieses Muster, wenn Sie mit StAX analysieren:
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;
}
Die Submethode verwendet also ungefähr den gleichen Ansatz, dh die Zählstufe:
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;
}
Und schließlich erreichen Sie eine Ebene, in der Sie die Basistypen lesen.
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;
}
Dies ist recht einfach und es gibt keinen Raum für Missverständnisse. Denken Sie daran, die Stufe korrekt zu verringern:
A. Nachdem Sie Zeichen erwartet haben, aber in einem Tag ein END_ELEMENT erhalten haben, das Zeichen enthalten sollte (im obigen Muster):
<Name>Thomas</Name>
war stattdessen
<Name></Name>
Das gleiche gilt auch für einen fehlenden Teilbaum, Sie bekommen die Idee.
B. nach dem Aufrufen von Subparsing-Methoden, die für Startelemente aufgerufen werden, und nach dem entsprechenden Endelement zurückgegeben wird, dh der Parser befindet sich auf einer Ebene niedriger als vor dem Methodenaufruf (das obige Muster).
Beachten Sie, dass bei diesem Ansatz auch "ignorierbare" Leerzeichen für eine robustere Implementierung völlig ignoriert werden.
Parser Verwenden
Sie Woodstox für die meisten Funktionen oder Aaalto-xml für die Geschwindigkeit.