Ändern von Dokumenten
(Auszug aus "Python & XML" von Christopher A. Jones & Fred L. Drake, Jr.)
Nachdem wir gesehen haben, wie man Informationen mit DOM aus unseren Dokumenten extrahieren kann, werden wir diese vermutlich auch ändern wollen. Es gibt nur wenige Dinge, die wir wissen müssen, um Änderungen vorzunehmen. Daher beschreiben wir erst die Grundoperationen und zeigen dann einige Beispiele. Die Grundoperationen bei der Modifikation eines Dokuments konzentrieren sich darauf, neue Knoten zu erzeugen, Knoten hinzuzufügen, zu verschieben und zu entfernen sowie den Inhalt von Knoten zu verändern. Da wir oft neue Elemente und Textinhalte hinzufügen wollen, beginnen wir mit der Erzeugung neuer Knoten.
Erzeugen neuer Knoten
Meistens müssen neue Knoten explizit erzeugt werden. Da DOM als Menge von Schnittstellen und nicht von konkreten Klassen definiert ist, ist der einzige Weg, neue Knoten zu erzeugen, der, Methoden auf den Objekten aufzurufen, die wir bereits haben. Glücklicherweise enthält die Document-Schnittstelle eine große Anzahl von Fabrikmethoden, mit denen wir die meisten Arten von neuen Knoten erzeugen können. (Methoden zum Erzeugen von Entity- und Notation-Knoten fehlen bemerkenswerterweise, aber die meisten Anwendungen sollten dadurch nicht eingeschränkt sein.)
Die meistverwendeten dieser Fabrikmethoden sind sehr einfach und werden zur Erzeugung neuer Element- und Textknoten benutzt. Für Elemente benutzen Sie die Methode createElement mit dem Tag-Namen des zu erzeugenden Elements als einzigem Parameter. Textknoten können mit der Methode createTextNode erzeugt werden, wobei man den Text des neuen Knotens als Parameter übergibt. Weitere Details zu den Fabrikmethoden für andere Knoten finden Sie im Referenzmaterial in Python-DOM-API.
Hinzufügen und Verschieben von Knoten
Es gibt einige sehr praktische Methoden für das Verschieben von Knoten an andere Stellen des Baums. Diese Methoden erscheinen in der grundlegenden Node-Schnittstelle, so daß alle DOM-Knoten darüber verfügen. Die Benutzung dieser Methoden unterliegt einigen Beschränkungen: Sie können damit keine Dokumente entwerfen, deren Struktur keinen Sinn macht, und die Wohlgeformtheit des Dokuments ist jederzeit garantiert. Es wird z. B. dann eine Ausnahme ausgelöst, wenn Sie versuchen, ein Kind an einen Textknoten anzufügen, oder wenn Sie versuchen, dem Document-Objekt ein zweites Kindelement anzufügen.
appendChild(newChild)
Nimmt ein newChild-Knotenargument und fügt es am Ende der Kinderliste des Knotens an.
insertBefore(newChild, refChild)
Nimmt den Knoten newChild und fügt ihn unmittelbar vor dem angegebenen Knoten refChild ein.
replaceChild(newChild, oldChild)
Ersetzt oldChild durch newChild und gibt oldChild an den Aufrufer zurück.
removeChild(oldChild)
Entfernt den Knoten oldChild aus der Kinderliste jenes Knotens, auf dem diese Methode aufgerufen wird.
Diese kurzen Beschreibungen ersetzen nicht die Referenzdokumentation für diese Methoden; siehe Python-DOM-API für vollständigere Informationen.
Löschen von Knoten
Sehen wir uns an, wie man einen Baum untersucht und bestimmte Knoten darin entfernt. Das folgende Beispiel verwendet einige verschachtelte Schleifen, um drei Ebenen tief in ein XML-Dokument hinabzusteigen, das mit dem Skript index.py aus dem Beispiel index.py erzeugt wurde. Das Design hat seine Grenzen, da angenommen wird, daß Sie nur mit Elementen einer maximalen Tiefe von drei hantieren, demonstriert aber jene Methoden von DOM, an denen wir interessiert sind.
Beispiel: domit.py
#!/usr/bin/env python
import sys
from xml.dom.ext.reader.Sax2 import FromXmlStream
from xml.dom.ext import PrettyPrint
# Hole DOM-Objekt
doc = FromXmlStream(sys.stdin)
# Entferne unerwünschte Knoten durch Traversieren des Knotenbaums
for node1 in doc.childNodes:
for node2 in node1.childNodes:
node3 = node2.firstChild
while node3 is not None:
next = node3.nextSibling
name = node3.nodeName
if name in ("contents", "extension", "userID", "groupID"):
# Entferne unerwünschte Knoten hier mittels Elternelement
node2.removeChild(node3)
node3 = next
PrettyPrint(doc)
Nachdem wir ein Dokument von der Standardeingabe erhalten haben, werden einige verschachtelte for-Schleifen ausgeführt, um drei Ebenen tief in den Baum hinunterzusteigen und nach bestimmten Tag-Namen zu suchen. Wenn Sie das Skript auf dem XML-Dokument ausführen, das wir mit index.py erzeugt haben, sollten Ihre file-Elemente so aussehen:
Der Leerraum um die entfernten Elemente herum bleibt erhalten, wie Sie an den Lücken zwischen den Elementen sehen können. Da wir nicht nach benachbarten Textknoten gesucht haben, sind sie nicht betroffen. Dieser Text war das Ergebnis eines Aufrufs der PrettyPrint-Funktion am Ende des Skripts. Natürlich sieht das Element gleich aus, unabhängig von der hierarchischen Position im Dokument. Wenn Sie DOM-Code schreiben, sollten Sie versuchen, ihn unabhängig von der Dokumentstruktur zu halten. Statt firstChild zu benutzen, um zu bekommen, was Sie suchen, versuchen Sie, die Kinder aufzuzählen und jedes zu untersuchen. Das mag etwas mehr Zeit brauchen, erlaubt aber mehr Flexibilität in der Dokumentstruktur. Solange das Zielelement unter dem Elternknoten auftaucht, wird das Kind gefunden werden. Wenn Sie firstChild benutzen, könnte der Ärger bereits vorprogrammiert sein, falls Ihnen jemand ein Dokument mit einer leicht abweichenden Struktur gibt, z. B. wenn ein gleichartiges Element vor einem anderen im Dokument vorkommt. Sie können diese Art von Operation mit einer rekursiven Funktion schreiben, damit Sie ähnliche Strukturen unabhängig von der Position im Dokument bearbeiten können. Und wenn es Ihnen wirklich egal ist, wo in einem Unterbaum ein Element gefunden wird, können Sie die bereits beschriebene Methode getElementsByTagName benutzen.
Eine andere häufige Anforderung besteht darin, einen Knoten zu finden, von dem Sie wissen, daß er ein Kind eines bestimmten Knotens sein muß, der aber nicht an einer bestimmten Stelle in der Kinderliste vorkommen muß. Eine einfache Schleife in einer Hilfsfunktion erledigt das auf elegante Weise:
from xml.dom import Node
def findChildrenByTagName(parent, tagname):
"""Liefert eine Liste von 'tagname'-Kindern von 'parent'."""
L = []
for child in parent.childNodes:
if (child.nodeType == Node.ELEMENT_NODE
and child.tagName == tagname):
L.append(child)
return L
Eine noch einfachere Hilfsfunktion, die sehr praktisch sein kann, ist eine Funktion, die das erste Kindelement mit einem bestimmten Tag-Namen findet oder das erste mit einem von mehreren Tag-Namen. Dies sind alles nur kleine Variationen der eben vorgestellten Funktion.
Ändern der Dokumentstruktur
Zusätzlich zu Ersetzungen und Ergänzungen können Sie ein Dokument mit DOM auch völlig neu strukturieren.
Im folgenden Beispiel ersetzen wir die verschachtelten Schleifen aus dem letzten Abschnitt durch eine rekursive Funktion. Das Skript funktioniert auch mit der XML-Ausgabe des Skipts index.py, das wir bereits zuvor in diesem Abschnitt verwendet haben. In dieser Version wird jedoch das size-Kind des file-Elements als Ersatz für sich selbst benutzt. Nach diesem Vorgang bleiben im Dokument nur noch directory- und size-Elemente übrig.
Dieses Beispiel zeigt domit2.py mit einer rekursiven Funktion.
Beispiel: domit2.py
#!/usr/bin/env python
from xml.dom.ext.reader.Sax2 import FromXmlStream
from xml.dom.ext import PrettyPrint
import sys
def makeSize(nodeList):
for subnode in nodeList:
if subnode.nodeType == subnode.ELEMENT_NODE:
if subnode.nodeName == "size":
subnode.parentNode.parentNode.replaceChild(
subnode, subnode.parentNode)
else:
makeSize(subnode.childNodes)
# Hole DOM-Objekt
doc = FromXmlStream(sys.stdin)
# Rufe Funktion auf
makeSize(doc.childNodes)
# Zeige verändertes Dokument an
PrettyPrint(doc)
Sie können das Skript von der Kommandozeile ausführen:
$> python domit2.py < wd.xml
Die Datei wd.xml ist eine mit dem Skript index.py erzeugte XML-Datei – Sie können jedoch eine beliebige Datei verwenden, solange sie die gleiche Struktur hat wie die von index.py erzeugten Dateien. Die Ausgabe sollte ungefähr wie folgt aussehen:
<Directory name='c:\windows\desktop\gl2'>
<size>230444</size>
<size>3035</size>
<size>8904</size>
<size>722</size>
<Directory name='c:\windows\desktop\gl2/Debug'>
<size>156672</size>
<size>86016</size>
<size>3779068</size>
<size>25685</size>
<size>17907</size>
<size>250508</size>
<size>208951</size>
<size>402432</size>
</Directory>
<size>3509</size>
<size>33792</size>
<size>722</size>
<size>48640</size>
<size>533</size>
</Directory>
Tipp der data2type-Redaktion: Zum Thema Python & XML bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an: |
Copyright © 2002 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Python & XML" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.
O’Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de