Wie bekomme ich einen Streaming-Iterator [Node] aus einem großen XML-Dokument?

8

Ich muss XML-Dokumente verarbeiten, die aus sehr vielen unabhängigen Datensätzen bestehen, z. B.

%Vor%

In einigen Fällen sind dies nur große Dateien, in anderen Fällen stammen sie aus einer Streaming-Quelle.

Ich kann nicht einfach scala.xml.XmlLoader.load () verwenden, weil ich nicht das gesamte Dokument im Speicher halten möchte (oder warten muss, bis der Eingabestream beendet wird), wenn ich nur mit einem arbeiten muss Rekord auf einmal. Ich weiß, dass ich XmlEventReader verwenden kann, um die Eingabe als eine Folge von XmlEvents zu streamen. Diese sind jedoch viel weniger bequem als scala.xml.Node.

Ich möchte also irgendwie einen faulen Iterator [Node] daraus machen, um mit der bequemen Scala-Syntax auf jedem einzelnen Datensatz zu operieren, während die Speichernutzung unter Kontrolle bleibt.

Um das selbst zu tun, könnte ich mit einem XmlEventReader beginnen, einen Puffer von Ereignissen zwischen jedem passenden Start- und End-Tag aufbauen und daraus eine Knoten-Struktur konstruieren. Aber gibt es einen leichteren Weg, den ich übersehen habe? Danke für alle Einsichten!

    
David Soergel 15.12.2011, 19:43
quelle

2 Antworten

8

Sie können den zugrunde liegenden Parser verwenden, der von XMLEventReader bis ConstructingParser verwendet wird, und Ihre Mitarbeiterknoten unterhalb der obersten Ebene mit einem Callback verarbeiten. Sie müssen nur vorsichtig sein, die Daten zu verwerfen, sobald sie verarbeitet werden:

%Vor%

Dann verwenden Sie so, um jeden Knoten auf der zweiten Ebene im konstanten Speicher zu verarbeiten (vorausgesetzt, die Knoten auf der zweiten Ebene erhalten keine willkürliche Anzahl von untergeordneten Elementen):

%Vor%

Der Vorteil gegenüber XMLEventReader ist, dass Sie nicht zwei Threads verwenden. Außerdem müssen Sie den Knoten im Vergleich zu Ihrer vorgeschlagenen Lösung nicht zweimal analysieren. Der Nachteil ist, dass dies auf der inneren Funktionsweise von ConstructingParser beruht.

    
huynhjl 16.12.2011, 04:31
quelle
5

Um von der Generator-Lösung von huynhjl zu TraversableOnce[Node] zu gelangen, verwenden Sie diesen Trick :

%Vor%

Das Ergebnis von generatorToTraversable kann nicht mehr als einmal durchlaufen werden (obwohl ein neuer ConstructingParser bei jedem Aufruf instanziiert wird), da der Eingabestream eine Quelle ist, die ein Iterator ist. Wir können Traversable.isTraversableAgain jedoch nicht überschreiben, da dies endgültig ist.

Wirklich möchten wir dies erzwingen, indem wir einfach einen Iterator zurückgeben. Traversable.toIterator und Traversable.view.toIterator erstellen jedoch einen Zwischen-Stream, der alle Einträge zwischenspeichert (wodurch der gesamte Zweck dieser Übung vereitelt wird). Naja; Ich lasse den Stream einfach eine Ausnahme auslösen, wenn zweimal darauf zugegriffen wird.

Beachten Sie auch, dass das Ganze nicht threadsicher ist.

Dieser Code läuft großartig, und ich glaube, dass die Gesamtlösung sowohl lazy als auch nicht caching ist (daher konstanter Speicher), obwohl ich es noch nicht mit einer großen Eingabe probiert habe.

    
David Soergel 16.12.2011 18:33
quelle

Tags und Links