XML-Masseneditierung - wie am Einfachsten umzusetzen

Hallo alle zusammen,

ich stehe im Moment vor der Aufgabe, eine große Menge (> 10.000) an XML-Dateien,
die immer nach dem selben Schema aufgebaut sind, auf die gleiche Weise editieren zu müssen,
da sich bei einem Datenexport ein Fehler eingeschlichen hatte.
Ein erneuter Export ist aber aufgrund von fortlaufenden Prozessnummern nicht möglich.

Das (korrekte) Schema ist im Moment wie folgt:

<?xml version="1.0" encoding="ISO-8859-1"?>
<meta>
<tabelle name="...">
<value column="Kundennummer">123456</value>
[...]
<value column="Produktnummer">654321</value>
<value column="Produktname">Brotaufstrich</value>
[...]
</tabelle>
</meta>

---

In den fehlerhaften XMLs ist "Produktname" nicht vorhanden und muss zusätzlich angelegt werden.
Der Inhalt von "Kundennummer" muss nach "Produktnummer", der Inhalt von "Produktnummer" nach "Produktname"
("Kundennummer" ist anschließend leer; das ist so beabsichtigt).

Wie lässt sich sowas am Einfachsten durchführen?
Es gibt Programme, mit denen soetwas "irgendwie" möglich ist, dann aber idR nur Datei für Datei.
Idee war ein Shell-Sript - allerdings fehlen mir für solche "Ausschneiden-Einfügen"-Geschichten die Kenntnisse...

Kann hier jemand helfen?

Viele Grüße
 
Hi,

hier ist ein kleines shellscript, mit dem Beispiel funktioniert es, aber das solltest du nochmal testen. Keine Gewaehr! ;)

Bevor du es ausprobierst, mach auf jeden Fall ein Backup aller xml-Dateien!

Die for-Schleife unten hab ich nicht getestet, also bitte vorher auf Testdateien probieren bis alles funktioniert.

Achtung: Die beiden snippets setzen voraus, dass in den Eintraegen Kundennummer und Produktnummer kein ';' und kein '<' steht, und dass die xml-Dateinamen keine Leerzeichen enthalten.

Code:
#!/bin/sh
# repair_xml.sh

XML_FILE="$1"

# Der Inhalt von "Kundennummer" muss nach "Produktnummer"
PRODUKTNUMMER="$(<"$XML_FILE" grep Kundennummer | tr '><' ';' | cut -d';' -f3)"

# der Inhalt von "Produktnummer" nach "Produktname"
PRODUKTNAME="$(<"$XML_FILE" grep Produktnummer | tr '><' ';' | cut -d';' -f3)"

# In den fehlerhaften XMLs ist "Produktname" nicht vorhanden und muss zusätzlich angelegt werden.

XML_PRODUKTNAME="$(<"$XML_FILE" grep Produktnummer | sed 's/Produktnummer/Produktname/g; s/\([^>]*>\)[^<]*\(<.*\)/\1'${PRODUKTNAME}'\2/')"

<"$XML_FILE" sed "/Kundennummer/s@\([^>]*>\)[^<]*\(<.*\)@\1\2@g; /Produktnummer/s@\([^>]*>\)[^<]*\(<.*\)@\1$PRODUKTNUMMER\2\n${XML_PRODUKTNAME}@g;"

Das Skript arbeitet nur auf einer Datei, sofern du es fuer alle Dateien ausfuehren willst, kannst du eine for-Schleife verwenden:

Code:
$ for xml in *xml; do sh ./repair_xml.sh "$xml" > "$xml.fixed"; done
 
Zuletzt bearbeitet:
Ich würde es einfach mit python und etree machen. 10000 sind jetzt nicht soo viele, wenn die nicht grade alle 1MiB haben.

PS: Warum zum teufel ISO-8859-1 ? UTF-8!!!!
 
Die Frage nach dem ISO/UTF habe ich auch oft gestellt - es ist so vorgegeben...

@ drm
Vielen vielen Dank sehr, das funktioniert soweit ganz gut.
Die Kundennummer ist leer - und im Feld der Produktnummer.

Der Produktname bereitet mir im Moment noch Kopfzerbrechen, da mit
XML_PRODUKTNAME="$(<"$XML_FILE" grep Produktnummer | sed 's/Produktnummer/Produktname/g; s/\([^>]*>\)[^<]*\(<.*\)/\1'${PRODUKTNAME}'\2/')"
<"$XML_FILE" sed "/Kundennummer/s@\([^>]*>\)[^<]*\(<.*\)@\1\2@g; /Produktnummer/s@\([^>]*>\)[^<]*\(<.*\)@\1$PRODUKTNUMMER\2\n${XML_PRODUKTNAME}@g;"
zwar unter Produktnummer eine Zeile frei ist, diese jedoch komplett unbefüllt bleibt.
 
Bei mir klappts mit dem Beispiel, das du angegeben hast. Falls die Zeile leer ist, scheint XML_PRODUKTNAME nicht gesetzt worden zu sein, kannst du die Variable mal ausgeben?
 
Was genau meinst du mit "die Variable mal ausgeben"?
Habe zuerst die Fehlermeldung übersehen:
sed: -e expression #1, char 61: unterminated `s' command
 
Ok. Am besten du kommentierst erstmal alles aus (indem du ein # vor die Zeilen setzt) und entfernst schrittweise einen Kommentar pro Durchlauf, um zu sehen, wo genau der Fehler auftritt. Variablen ausgeben kannst du mit >echo "$VARIABLE"<. Hier ist das Programm fuer den ersten Durchlauf:

Code:
#!/bin/sh
# repair_xml.sh

XML_FILE="$1"

# Der Inhalt von "Kundennummer" muss nach "Produktnummer"
PRODUKTNUMMER="$(<"$XML_FILE" grep Kundennummer | tr '><' ';' | cut -d';' -f3)"
echo "$PRODUKTNUMMER"

# der Inhalt von "Produktnummer" nach "Produktname"
#PRODUKTNAME="$(<"$XML_FILE" grep Produktnummer | tr '><' ';' | cut -d';' -f3)"

# In den fehlerhaften XMLs ist "Produktname" nicht vorhanden und muss zusätzlich angelegt werden.

#XML_PRODUKTNAME="$(<"$XML_FILE" grep Produktnummer | sed 's/Produktnummer/Produktname/g; s/\([^>]*>\)[^<]*\(<.*\)/\1'${PRODUKTNAME}'\2/')"

#<"$XML_FILE" sed "/Kundennummer/s@\([^>]*>\)[^<]*\(<.*\)@\1\2@g; /Produktnummer/s@\([^>]*>\)[^<]*\(<.*\)@\1$PRODUKTNUMMER\2\n${XML_PRODUKTNAME}@g;"
 
Ah ok, danke sehr.
Produktnummer wird korrekt ausgegeben; ebenso der richtige Produktname.
Beim nächsten Schritt tritt dann der beschriebene Fehler auf.
 
Funktioniert es hiermit?

Code:
#!/bin/sh
# repair_xml.sh

XML_FILE="$1"

# Der Inhalt von "Kundennummer" muss nach "Produktnummer"
PRODUKTNUMMER="$(<"$XML_FILE" grep Kundennummer | tr '><' ';' | cut -d';' -f3)"

# der Inhalt von "Produktnummer" nach "Produktname"
PRODUKTNAME="$(<"$XML_FILE" grep Produktnummer | tr '><' ';' | cut -d';' -f3)"

# In den fehlerhaften XMLs ist "Produktname" nicht vorhanden und muss zusätzlich angelegt werden.
XML_PRODUKTNAME="$(<"$XML_FILE" grep Produktnummer | sed -e 's/Produktnummer/Produktname/g' -e 's/\([^>]*>\)[^<]*\(<.*\)/\1'${PRODUKTNAME}'\2/')"

<"$XML_FILE" sed -e "/Kundennummer/s@\([^>]*>\)[^<]*\(<.*\)@\1\2@g" -e "/Produktnummer/s@\([^>]*>\)[^<]*\(<.*\)@\1$PRODUKTNUMMER\2\n${XML_PRODUKTNAME}@g;"
 
Mal ehrlich, hört auf XML mit Regexes zu bearbeiten, das zerstört in mir jede Hoffnung, dass unsere Zunft nochmal irgendwas vorzeigbares produziert.

Code:
from lxml import etree

doc = etree.parse("test.xml")
for tabelle in doc.xpath("//tabelle"):
    kundennummer = tabelle.xpath('./value[@column="Kundennummer"]')[0]
    produktnummer = tabelle.xpath('./value[@column="Produktnummer"]')[0]
    pname = etree.Element('value', {'column':'Produktname'})
    pname.text = produktnummer.text
    produktnummer.text = kundennummer.text
    kundennummer.text = ""
    tabelle.append(pname)


print etree.tounicode(doc)
 
Hast ja recht, ich hab nur noch nie mit XML gearbeitet und das Beispiel schien mir simpel genug, um meine regex-skills auffrischen :D
 
Ich habe mein Skript inzwischen aktualisiert.

In den fehlerhaften XMLs ist "Produktname" nicht vorhanden und muss zusätzlich angelegt werden.
Der Inhalt von "Kundennummer" muss nach "Produktnummer", der Inhalt von "Produktnummer" nach "Produktname"
("Kundennummer" ist anschließend leer; das ist so beabsichtigt).

Das Kommando würde da so aussehen:
Code:
awk -f xml.awk foobar.xml -search:value -renameAttrib:Produktnummer=Produktname -renameAttrib:Kundennummer=Produktnummer -attrib:Kundennummer= -select:/ -print

Der neue Code kommt auf stdout raus. Mit find -exec, apply oder xargs könnte man sich da was passendes zurechtbasteln.
 
Guten Morgen zusammen,

es hat funktioniert - vielen vielen Dank!
@drm: mein Fehler - durch unvollständiges Kopieren der Zeilen hat sich ein Synthaxfehler eingeschlichen...
@Kamikaze: das habe ich gestern abend aus Neugier auch mal reingedattelt und auch das funktioniert einwandfrei.
Gefällt mir persönlich ein bisschen bessser, da für mich logischer vom Aufbau und wesentlich kürzer formuliert.

Danke euch beiden nochmal!!!
Das hat mir eine Menge Zeit gespart!

Viele Grüße und ein schönes Wochenende!
 
Zurück
Oben