Techniken zum Parsen von XML

11

Ich fand XML immer etwas umständlich zu verarbeiten. Ich spreche nicht über die Implementierung eines XML-Parsers: Ich spreche über die Verwendung eines vorhandenen Stream-basierten Parsers wie eines SAX-Parsers, der das XML Knoten für Knoten verarbeitet.

Ja, es ist wirklich einfach, die verschiedenen APIs für diese Parser zu lernen, aber wenn ich mir Code anschaue, der XML verarbeitet, finde ich ihn immer etwas kompliziert. Das wesentliche Problem scheint zu sein , dass ein XML - Dokument logisch in einzelne Knoten getrennt ist, und doch sind die Datentypen und Attribute werden oft von den tatsächlichen Daten, die manchmal durch mehrere Ebenen der Verschachtelung getrennt. Daher muss bei der individuellen Verarbeitung eines bestimmten Knotens viel zusätzlicher Status beibehalten werden, um zu bestimmen, wo wir uns befinden und was wir als Nächstes tun müssen.

Beispiel: Ein Ausschnitt aus einem typischen XML-Dokument:

<book>
  <title>Blah blah</title>
  <author>Blah blah</author>
  <price>15 USD</price>
</book>

... Wie würde ich feststellen, wann ich auf einen Textknoten mit einem Buchtitel gestoßen bin? Angenommen, wir haben einen einfachen XML-Parser, der sich wie ein Iterator verhält und uns bei jedem Aufruf den nächsten Knoten im XML-Dokument gibt XMLParser.getNextNode(). Ich schreibe unweigerlich Code wie den folgenden:

boolean insideBookNode = false;
boolean insideTitleNode = false;

while (!XMLParser.finished())
{
    ....
    XMLNode n = XMLParser.getNextNode();

    if (n.type() == XMLTextNode)
    {
        if (insideBookNode && insideTitleNode)
        {
            // We have a book title, so do something with it
        }
    }
    else
    {
        if (n.type() == XMLStartTag)
        {
            if (n.name().equals("book")) insideBookNode = true
            else if (n.name().equals("title")) insideTitleNode = true;
        }
        else if (n.type() == XMLEndTag)
        {
            if (n.name().equals("book")) insideBookNode = false;
            else if (n.name().equals("title")) insideTitleNode = false;
        }
    }
}

Grundsätzlich wird die XML-Verarbeitung schnell zu einer riesigen, von der Zustandsmaschine gesteuerten Schleife, in der viele Statusvariablen verwendet werden, um übergeordnete Knoten anzuzeigen, die wir zuvor gefunden haben. Andernfalls muss ein Stapelobjekt verwaltet werden, um alle verschachtelten Tags zu verfolgen. Dies wird schnell fehleranfällig und schwierig zu warten.

Wieder scheint das Problem zu sein, dass die Daten, an denen wir interessiert sind, nicht direkt einem einzelnen Knoten zugeordnet sind. Sicher, es könnte sein, wenn wir das XML wie folgt schreiben:

<book title="Blah blah" author="blah blah" price="15 USD" />

... aber so wird XML in der Realität selten verwendet. Meistens haben wir Textknoten als untergeordnete Knoten von übergeordneten Knoten, und wir müssen die übergeordneten Knoten verfolgen, um zu bestimmen, worauf sich ein Textknoten bezieht.

Also ... mache ich etwas falsch? Gibt es einen besseren Weg? Ab wann wird die Verwendung eines XML-Stream-basierten Parsers zu umständlich, sodass ein vollwertiger DOM-Parser erforderlich wird? Ich würde gerne von anderen Programmierern hören, welche Art von Redewendungen sie bei der Verarbeitung von XML mit Stream-basierten Parsern verwenden. Muss Stream-basiertes XML-Parsing immer zu einer riesigen Zustandsmaschine werden?

Channel72
quelle
2
Wenn Sie eine .net-Sprache verwenden, sollten Sie sich linq to xml aka XLinq ansehen.
Muad'Dib
Danke, ich dachte, ich wäre der einzige mit diesem Problem. Ehrlich gesagt empfinde ich das gesamte XML-Format oft eher als Hindernis als als Hilfe. Ja, es ermöglicht das Speichern vieler strukturierter Daten in einer kleinen Textdatei. Aber wenn Sie dann mehr als 20 Klassen benötigen, um die Sache auszupacken und zu verstehen - ohne Garantie, dass Sie etwas mehr oder weniger Wichtiges nicht übersehen. Es ist wie der Hase in Monty Pythons Heiligem Gral.
Elise van Looij

Antworten:

9

Für mich ist die Frage umgekehrt. Ab wann wird ein XML-Dokument so umständlich, dass Sie SAX anstelle von DOM verwenden müssen?

Ich würde SAX nur für einen sehr großen Datenstrom mit unbestimmter Größe verwenden. oder wenn das Verhalten, das das XML aufrufen soll, wirklich ereignisgesteuert und daher SAX-ähnlich ist.

Das Beispiel, das Sie geben, sieht für mich sehr DOM-artig aus.

  1. Laden Sie das XML
  2. Extrahieren Sie die Titelknoten und "machen Sie etwas mit ihnen".

BEARBEITEN: Ich würde SAX auch für Streams verwenden, die möglicherweise fehlerhaft sind, aber wo ich die Daten am besten erraten möchte.

Paul Butcher
quelle
2
Ich denke, das ist ein guter Punkt. Wenn Sie Dokumente analysieren, die für DOM zu groß sind, müssen Sie überlegen, ob Sie Dokumente analysieren, die für XML
Dean Harding
1
+1: Wenn ich die Option hätte, würde ich immer mit DOM gehen. Leider scheinen unsere Designanforderungen immer "Fähigkeit, Dokumente jeder Größe zu verarbeiten" und "muss performant sein" zu umfassen, was DOM-basierte Lösungen so gut wie ausschließt.
TMN
3
@TMN, in einer idealen Welt, in der Anforderungen XML überhaupt ausschließen würden.
SK-Logik
1
@TMN, das klingt nach einer dieser Phantomanforderungen: "Natürlich sind alle unsere Dokumente nur etwa 100 KB groß, und das größte, das wir gesehen haben, ist 1 MB, aber Sie wissen nie, was die Zukunft bringt, deshalb sollten wir unsere Optionen offen halten und für unendlich große Dokumente bauen "
Paul Butcher
@ Paul Butcher, du weißt es nie. Ich meine, ein Dump von Wikipedia entspricht 30 GB XML.
Channel72
7

Ich arbeite nicht zu viel mit XML, meiner Meinung nach ist die Verwendung von XPath eine der besten Möglichkeiten, XML mit einer Bibliothek zu analysieren.

Anstatt den Baum zu durchlaufen, um einen bestimmten Knoten zu finden, geben Sie ihm einen Pfad. Im Fall Ihres Beispiels (im Pseudocode) wäre es ungefähr so:

books = parent.xpath ("/ book") // Dies würde Ihnen alle Buchknoten geben
für jedes Buch in Büchern
    title = book.xpath ("/ title / text ()")
    author = book.xpath ("/ author / text ()")
    price = book.xpath ("/ price / text ()")

    // Mach Dinge mit den Daten

XPath ist viel leistungsfähiger. Sie können anhand von Bedingungen (sowohl nach Werten als auch nach Attributen) suchen, einen bestimmten Knoten in einer Liste auswählen und Ebenen durch den Baum verschieben. Ich empfehle Ihnen, nach Informationen zur Verwendung zu suchen. Es ist in vielen Parsing-Bibliotheken implementiert (ich verwende es in der .Net Framework-Version und in lxml für Python).

Ioachim
quelle
Das ist in Ordnung, wenn Sie im Voraus wissen und vertrauen können, wie die XML strukturiert ist. Wenn Sie nicht wissen, ob beispielsweise die Breite eines Elements als Attribut eines Knotens oder als Attributknoten innerhalb des Größenknotens eines Elements angegeben wird, ist XPath keine große Hilfe.
Elise van Looij
5

Muss Stream-basiertes XML-Parsing immer zu einer riesigen Zustandsmaschine werden?

Normalerweise schon.

Wenn ich auf die Verwendung eines vollwertigen DOM-Parsers hinweisen möchte, muss ich Teile der Dateihierarchie im Speicher nachahmen, um beispielsweise Querverweise innerhalb des Dokuments auflösen zu können.

Alexander Gessler
quelle
+1: Beginnen Sie mit DOM. Vermeiden Sie SAX.
S.Lott
oder mit vtd-xml
vtd-xml-author
4

Das Parsen im Allgemeinen steuert einfach eine Zustandsmaschine, und das XML-Parsen ist nicht anders. Stream-basiertes Parsen ist immer ein Problem. Ich baue immer einen Stapel auf, um die Ahnenknoten zu verfolgen, und definiere viele Ereignisse und eine Art Ereignis-Dispatcher, der eine Tag- oder Pfadregistrierung überprüft und ein Ereignis auslöst wenn man passt. Der Kerncode ist ziemlich eng, aber ich habe ein riesiges Bündel von Ereignishandlern, die hauptsächlich darin bestehen, einem Feld in einer Struktur irgendwo den Wert des folgenden Textknotens zuzuweisen. Es kann ziemlich haarig werden, wenn Sie dort auch Geschäftslogik mischen müssen.

Ich würde immer DOM verwenden, es sei denn, Größen- oder Leistungsprobleme diktieren etwas anderes.

TMN
quelle
1

Nicht vollständig sprachunabhängig, aber ich deserialisiere das XML normalerweise in Objekte, anstatt überhaupt über das Parsen nachzudenken. Wenn Sie ein Geschwindigkeitsproblem haben, müssen Sie sich nur Gedanken über Parsing-Strategien an sich machen.

Wyatt Barnett
quelle
Das fällt unter Parsing. Es sei denn, das betreffende XML ist die Ausgabe der Objektserialisierung und Sie verfügen über eine vorgefertigte Deserialisierungsbibliothek. Aber dann erscheint diese Frage nicht.
Viele Sprachen / Stapel verfügen über bereits erstellte Deserialisierungsbibliotheken.
Wyatt Barnett
Ja, na und? Meine Punkte noch halten - nicht alle XML - Dateien in der freien Natur kommen in einem solchen Format, und wenn Sie eine haben , das tut man nicht fragen , diese Frage , wie Sie genau das Deserialisierung - Bibliothek verwenden und nicht Parse etwas auf eigene Faust, aus Streams oder auf andere Weise.
0

Es wird viel weniger umständlich, wenn Sie XPath verwenden können. Und im .Net Land abstrahiert LINQ to XML auch viele der weniger glamourösen Dinge. ( Bearbeiten - diese erfordern natürlich einen DOM-Ansatz)

Wenn Sie einen Stream-basierten Ansatz verfolgen (Sie können also keine schöneren Abstraktionen verwenden, für die ein DOM erforderlich ist), denke ich, dass dies immer ziemlich umständlich sein wird, und ich bin mir nicht sicher, ob es einen Ausweg gibt.

Steve
quelle
Wenn Sie XPath verwenden, verwenden Sie DOM (es sei denn, Sie verwenden es mit einem selbst entwickelten XPath-Evaluator).
TMN
Ja, daher mein Kommentar zu den Abstraktionen, die DOM erfordern ... aber ich werde es klarstellen, danke!
Steve
0

Wenn Sie einen Parser finden, der Ihnen einen Iterator gibt, haben Sie darüber nachgedacht, ihn als Lexer zu behandeln und einen Zustandsmaschinengenerator zu verwenden?

Demi
quelle