Hi zusammen,
wie schon in meinem anderen Thread angeschnitten, bin ich gerade am Transformieren von XML zu JSON mittels pyMARC.
Mir liegt dabei ein großer Datenbankabzug als .xml vor: 1 Mio+ Datensätze, Größe: mehrere GB. Die Verarbeitung als Einzeldatei ist mir nicht möglich, weil sie die gängigen RAM-Kapazitäten der mir zur Verfügung stehenden Geräte sprengt.
Es braucht also eine Prozessierung in kleineren Chunks, um den Arbeitsspeicher zu schonen. Dabei auf gängige XML-Parser in Python zurückzugreifen, um damit die XML-Datei zu splitten (Stichwort: XML-Splitter) ist leider ebenso ineffizient, weil es bei dieser Größe zu lange braucht. Spezielle und vor allem proprietäre Software schließe ich aus, weil der Prozess der Vor-Verarbeitung nahtlos an ein Python-Skript angebunden sein soll, das dann die Transformation leistet. Bleiben also die Linux/Unix-Kommandozeilen-Tools, die mit dem Rohtext der XML-Datei arbeiten, ohne diesen zu parsen.
Hier noch mal die Struktur der XML-Quelldatei:
<?xml version="1.0" encoding="ISO-8859-1"?>
<collection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="abc.xsd">
<record identifier="xyz" status="e" level="p">
[...]
</record>
<record identifier="hij" status="e" level="p">
[...]
</record>
<record identifier="klm" status="e" level="p">
[...]
</record>
</collection>
Mit Anlehnung an eine Herangehensweise aus dem Data Wrangler Handbuch bin ich so vorgegangen:
- Entferne alle unnötigen Umbrüche zwischen den Datensätzen
sed -i '/<\/record>/,/<record/{//!d}' bigdatadump.xml
- Ersetze alle Zeilenumbrüche mit einem ungenutzten Zeichen (notwendig für das spätere korrekte Splitten) und speichere als Arbeitskopie
cat bigdatadump.xml | tr '\n' '\007' > workingfile
- Setze einen Zeilenumbruch nach jeden
</record>
- Ab dann steht jeder Datensatz auf jeweils einer Zeile und lässt sich so später korrekt splitten
sed -i 's/<\/record>/\0\n/g' workingfile
- Entferne alles, was am Anfang der Datei vor dem ersten
<record>
steht (XML-Header etc.)
sed -i 's/^<?xml.*<record/<record/' workingfile
- Entferne alle Leerzeichen zwischen den Elementen
sed -i 's/>[\t \x07]*</></g' workingfile
- Teile die Datei in 100MB Chunks
split --line-bytes=100MB workingfile workingfile.
- Abschließend: Mache die Zeilenumbruch-Ersetzungen von Schritt 2 überall wieder rückgängig, damit die Dateien wieder normale Zeilenumbrüche haben
for i in workingfile.*; do
cat $i | tr '\007' '\n' > $i.xml
done
Im Resultat sollte man nun lauter 100MB große XML-Dateien (workingfile.aa.xml, workingfile.ab.xml, workingfile.ac.xml etc.) haben, die insich geschlossene Datensätze enthalten und dann Stück für Stück transformiert werden können. Würde man nur Schritt 6 (split) direkt auf die XML-Quelldatei anwenden, wäre die Datei zwar auch in Chunks gesplittet, jedoch an willkürlichen Stellen, wodruch die XML-Struktur gebrochen und damit fehlerhaft ist.
Leider scheitert der oben von mir beschriebene Ansatz in meinem Fall immer noch an der Größe der XML-Datei. Bei Schritt 3 wird der folgende Fehler geworfen: „sed: regex input buffer length larger than INT_MAX“
Daher die Frage, an alle ETL-Erfahrenen hier: Wie geht ihr mit großen XML-Datenabzügen um, wenn ihr diese Transformieren wollt? Wie komme ich hier bei extrem großen Quelldateien mit Kommadozeilen-Tools weiter?
Vielen Dank im Voraus.