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?
quelle
Antworten:
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.
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.
quelle
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:
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).
quelle
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.
quelle
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.
quelle
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.
quelle
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.
quelle
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?
quelle