Streaming-XML-Parser wie SAX und StAX sind schneller und speichereffizienter als Parser, die eine Baumstruktur wie DOM-Parser erstellen. SAX ist ein Push-Parser, dh es handelt sich um eine Instanz des Beobachtermusters (auch als Listener-Muster bezeichnet). SAX war zuerst da, aber dann kam StAX - ein Pull-Parser, was bedeutet, dass er im Grunde wie ein Iterator funktioniert.
Sie können Gründe finden, warum Sie StAX überall SAX vorziehen, aber es läuft normalerweise darauf hinaus: "Es ist einfacher zu bedienen".
Im Java-Tutorial zu JAXP wird StAX vage als Mitte zwischen DOM und SAX dargestellt: "Es ist einfacher als SAX und effizienter als DOM". Ich habe jedoch nie Hinweise darauf gefunden, dass StAX langsamer oder weniger speichereffizient als SAX ist.
All dies hat mich gefragt: Gibt es Gründe, SAX anstelle von StAX zu wählen?
Ü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(...); // A
oder
void parseAddress(...); // B
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.
// method in standard StAX reader int event = reader.next(); if(event == XMLStreamConstants.START_ELEMENT) { // .. your logic here for 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(..) { // 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; }
Die Submethode verwendet also ungefähr den gleichen Ansatz, dh die Zählstufe:
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; }
Und schließlich erreichen Sie eine Ebene, in der Sie die Basistypen lesen.
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; }
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.
quelle
@Rinke: Ich denke nur, wenn ich daran denke, SAX gegenüber STAX zu bevorzugen, falls Sie XML-Inhalte nicht verarbeiten müssen. Zum Beispiel möchten Sie nur überprüfen, ob eingehendes XML in Ordnung ist, und nur Fehler behandeln, wenn dies der Fall ist. In diesem Fall können Sie einfach die parse () -Methode im SAX-Parser aufrufen und den Fehlerhandler angeben, um alle zu behandeln Parsing-Problem ... Grundsätzlich ist STAX in Szenarien, in denen Sie mit Inhalten umgehen möchten, definitiv die bevorzugte Wahl, da der SAX-Content-Handler zu schwer zu codieren ist ...
Ein praktisches Beispiel für diesen Fall kann sein, wenn Sie eine Reihe von SOAP-Knoten in Ihrem Unternehmenssystem haben und ein SOAP-Knoten der Einstiegsklasse nur SOAP-XML durch die nächste Stufe laufen lässt, die wohlgeformt ist. Dann sehe ich keinen Grund, warum ich würde STAX verwenden. Ich würde nur SAX verwenden.
quelle
Es ist alles ein Gleichgewicht.
Sie können einen SAX-Parser mithilfe einer Blockierungswarteschlange und einiger Thread-Tricks in einen Pull-Parser verwandeln. Für mich gibt es also viel weniger Unterschiede, als es zunächst scheint.
Ich glaube, dass StAX derzeit in einem Glas eines Drittanbieters verpackt werden muss, während SAX in Javax kostenlos ist.
Ich habe mich kürzlich für SAX entschieden und einen Pull-Parser erstellt, damit ich mich nicht auf ein Glas eines Drittanbieters verlassen musste.
Zukünftige Versionen von Java werden mit ziemlicher Sicherheit eine StAX-Implementierung enthalten, sodass das Problem behoben ist.
quelle
Mit StAX können Sie bidirektionale XML-Parser erstellen, die schnell sind. Es ist eine bessere Alternative zu anderen Methoden wie DOM und SAX, sowohl hinsichtlich der Leistung als auch der Benutzerfreundlichkeit
Weitere Informationen zu StAX finden Sie in den Java StAX-Tutorials
quelle
Die meisten Informationen in diesen Antworten sind etwas veraltet. In diesem Forschungsbericht aus dem Jahr 2013 wurden alle XML-Parsing-Bibliotheken umfassend untersucht. Lesen Sie sie und Sie werden leicht den klaren Gewinner sehen (Hinweis: Es gibt nur einen wahrer Gewinner) ...
http://recipp.ipp.pt/bitstream/10400.22/1847/1/ART_BrunoOliveira_2013.pdf
quelle
XMLStreamReader
.