Wann sollte ich SAX gegenüber StAX wählen?

81

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?

Rinke
quelle

Antworten:

22

Um es ein bisschen zu verallgemeinern, ich denke, es StAXkann so effizient sein wie SAX. Mit dem verbesserten Design von StAXkann ich keine Situation finden, in der das SAXParsen bevorzugt wäre, es sei denn, ich arbeite mit Legacy-Code.

BEARBEITEN : Laut diesem Blog bieten Java SAX vs. StAX StAX keine Schemaüberprüfung.

Johan Sjöberg
quelle
2
Es ist nicht allzu schwer, eine Validierung zusätzlich zu stax hinzuzufügen. habe das neulich selbst umgesetzt.
Jtahlborn
Weitere Details zur Validierung: stackoverflow.com/questions/5793087/stax-xml-validation
Ben
81

Ü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.

ThomasRS
quelle
In Ihrer Eröffnungsrede steht "... während in SAX ...". Ist das ein Tippfehler? ("SAX" statt "StAX") Auf jeden Fall danke für die Antwort. Wenn ich Sie richtig verstehe, sagen Sie, dass der implizite Status im SAX-Ansatz ein Vorteil gegenüber der Notwendigkeit ist, Ihren XML-Baumspeicherort im StAX-Ansatz zu verfolgen.
Rinke
Vielen Dank für die (jetzt noch ausführlichere) Antwort. Ich fürchte, ich sehe immer noch nicht, was ein guter Grund wäre, SAX anstelle von StAX zu verwenden. Ihre Antwort ist eine gute Erklärung für die Funktionsweise beider Prozessoren.
Rinke
Für einfache Dokumente sind sie gleich. Schauen Sie sich zum Beispiel dieses Schema an: mpeg.chiariglione.org/technologies/mpeg-21/mp21-did/index.htm und StAX werden praktischer sein.
ThomasRS
Kurz gesagt, da Sie bereits Ihren Code schreiben, verstehen Sie, welcher Teil des Dokuments, den Sie analysieren, dh alle Logik zum Zuordnen eines SAX-Ereignisses zu korrektem Code, verschwendet wird.
ThomasRS
16

@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.

ag112
quelle
Ich habe diese Antwort als die bisher beste ausgewählt. Obwohl es eine gute Antwort ist, halte ich es nicht für 100% maßgeblich und klar. Neue Antworten sind willkommen.
Rinke
1

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.

OldCurmudgeon
quelle
1
Java SE 6 enthält StAX. Aber zB Android-Implementierung enthält es nicht.
Bjarne Boström
0

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

Annamalai Thangaraj
quelle
-1

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

vtd-xml-author
quelle
1
Ich habe die Zeitung gelesen, der Gewinner ist StAX mit der Cursor-API wie in XMLStreamReader.
Roland
sehr lustig :), du meinst den Gewinner des Schildkrötenrennens :)
vtd-xml-author
Ich habe gerade das Papier noch einmal gelesen, und ja, StaX ist vtd überlegen, schneller und weniger Speicherverbrauch. Also, was ist dein Punkt?
Roland
Der Gewinner ist stAX in welcher Weise? Auf welchen Teil des Papiers beziehen Sie sich? Dokument ändern oder auswählen oder differenzieren? anscheinend hat der Autor des Papiers eine andere Schlussfolgerung gezogen. aber sie könnten völlig falsch sein ...
vtd-xml-author
1
zB Seite 80: Den Ergebnissen (Abbildung 11 und Abbildung 12) zufolge können wir sehen, dass StAX die API mit der besseren Leistung ist, gefolgt von VTD. VTD verbraucht jedoch eine beträchtliche Menge an Speicher. Der Speicherverbrauch kann ein Engpass für Umgebungen sein, die eingeschränkte Funktionen bieten.
Roland