XML kann Daten strukturieren. Dazu verwendet XML sogenannte Tags. Ein Tag wird in spitze Klammern, also Kleiner- und Größerzeichen, eingeschlossen. Das Ende wird durch ein End-Tag definiert, das direkt nach dem Kleinerzeichen einen Schrägstrich besitzt.
Am einfachsten ist eine XML-Datei am Beispiel zu verstehen. Die folgende XML-Datei enthält eine Kundenliste. Diese wird unter dem Namen kundenliste abgelegt. In der Kundenliste können mehrere Kunden abgelegt werden. Jeder von denen wird durch das Tag kunde eingeschlossen. Jeder der Kunden hat die Felder kdnr, name und name. Die vorliegende XML-Datei ist also beispielsweise dazu geeignet eine Datenbanktabelle für Kunden zu sichern und auch wieder zu füttern.
<?xml version="1.0" encoding="UTF-8"?> <kundenliste> <kunde> <kdnr>1234</kdnr> <name>Reiner Zufall</name> <anschrift>Auf dem Holzweg, Buxtehude</anschrift> </kunde> <kunde> <kdnr>1235</kdnr> <name>Maria Klug</name> <anschrift>Einsteinring 2, Gintoft</anschrift> </kunde> </kundenliste>Die erste Zeile ist sehr speziell, weil sie den Standard und die Zeichenkodierung festlegt. Aber sie zeigt auch, dass Tags Attribute haben können und wie sie gesetzt werden. Hier beispielsweise hat xml zwei Attribute, nämlich version und encoding, denen durch ein Gleichheitszeichen Werte zugewiesen werden.
XML-Deklaration
Die erste Zeile bestimmt den Standard, nach dem sich die XML-Datei richtet. Dabei ist für die internationalen Sonderzeichen vor allem der Encoding-Standard wichtig, wie hier UTF-8.<?xml version="1.0" encoding="UTF-8"?>
Ein Tag
Ein Tag umschließt einen Bereich und kann verschachtelt werden.<aussen> <innen></innen> <nochwas></nochwas> </aussen>Die Einrückung und Anordnung der Tags ist grundsätzlich frei gestaltbar. Da aber von Zeit zu Zeit auch Menschen diese Dateien lesen müssen, ist eine übersichtliche Einrückung schon hilfreich. Tags können Attribute haben. Hier hat das Gehalt das Attribut typ, das mit dem Wert float besetzt ist.
<gehalt typ=float> 1200.00 </gehalt>Eine Kurzform ist möglich, vor allem, wenn das Tag nur Attribute enthält.
<gehalt typ=float wert=1200.00 />Da es sich bei XML um reine Textdateien handelt, wäre es natürlich möglich, die Dateien mit den Standarddateizugriffen zu lesen und den Inhalt auszuwerten. Eine solche Auswertung würde dann auch schnell zu einer Rekursion führen und die Gebiete des Compilerbaus berühren. Das ist nicht jedermanns Sache. Aus diesem Grund haben sich Bibliotheken entwickeln die den Umgang mit XML beherrschen. Sie werden hier DOM und SAX kennenlernen.
XML-Dateien auslesen mit DOM
DOM steht für Document Object Model. Dieses Verfahren liest die XML-Datei komplett in den Speicher, um sie dann auswerten zu können. Das hat bei großen XML-Dateien den Nachteil, dass sehr viel Speicher belegt wird.Kurz und knapp
Um den Umgang mit DOM an einem einfachen Beispiel zu demonstrieren, verwenden wir die folgende relativ unsinnige XML-Datei, die unter dem Namen items.xml vorliegen soll.<data> <items> <item name="item1">Inhalt1</item> <item name="item2">Inhalt2</item> <item name="item3">Inhalt3</item> <item name="item4">Inhalt4</item> </items> </data>Das folgende Programm liest die Datei ein und sucht dann das Tag item. Aus der Liste wird dann das jeweilige Attribut name ausgelesen und über childnotes der eigentliche Inhalt. Beides wird auf dem Bildschirm angezeigt.
from xml.dom import minidom xmldoc = minidom.parse('items.xml') itemlist = xmldoc.getElementsByTagName('item') print(len(itemlist)) print(itemlist[0].attributes['name'].value) for s in itemlist : print(s.attributes['name'].value+":", s.childNodes[0].nodeValue)
Die Kundenliste
Nachdem die einfache Auswertung geklappt hat, wenden wir und dem Beispiel mit der bereits vorgestellten Kundenliste zu. Vor dem Auslesen der XML-Datei soll erst einmal eine Klasse für den Kunden definiert werden. Das ist zwar prinzipiell nicht erforderlich, ist aber für die Weiterverarbeitung sinnvoll.class Kunde: def __init__(self): __kdnr = 0 __name = "" __anschrift = "" def setKdnr(self, kdnr): self.__kdnr = kdnr def setName(self, name): self.__name = name def setAnschrift(self, anschrift): self.__anschrift = anschrift def __str__(self): # um per print ausgeben zu koennen return str(self.__kdnr)+"|"+self.__name \ +"|"+self.__anschriftDie Bibliothek xml.dom enthält den Parser minidom, der für die meisten Anwendungen ausreichend ist.
In der Importzeile wird minidom mit dem Befehl as umbenannt, damit der Parser im Programm einfach als dom angesprochen werden kann. Das vereinfacht einen später vielleicht doch notwendigen Wechsel des Parsers.
Die Funktion leseKunde() erledigt die eigentliche Arbeit.
import xml.dom.minidom as dom def leseKunde(dateiname): KundenDict = {} baum = dom.parse(dateiname) for eintrag in baum.firstChild.childNodes: if eintrag.nodeName == "kunde": kunde = Kunde() for knoten in eintrag.childNodes: if knoten.nodeName == "kdnr": kdnr = knoten.firstChild.data kunde.setKdnr(kdnr) elif knoten.nodeName == "name": kunde.setName( knoten.firstChild.data.strip()) elif knoten.nodeName == "anschrift": kunde.setAnschrift( knoten.firstChild.data.strip()) KundenDict[kdnr] = kunde return KundenDict kundenliste = leseKunde("kundenliste.xml") for i in kundenliste: print.kundenliste[i]
XML erzeugen mit DOM
Wenn aus internen Daten XML-Dateien erzeugt werden sollen, kann DOM ebenfalls helfen. Das folgende Beispielprogramm erzeugt aus einer Kundenliste, die zunächst vom Programm erzeugt wird, eine XML-Datei. Dazu wird mit der Funktion createDocument() eine DOM-Struktur erzeugt und Element für Element über die Funktion createElement() erzeugt und dann per appendChild() eingehängt.Nachdem die Struktur fertig ist, wird sie mit der Funktion writexml() weggeschrieben.
import xml.dom def schreibeKunden(kundenliste, dateiname): domImpl = xml.dom.getDOMImplementation() doc = domImpl.createDocument(None, "kundenliste", None) for kdnr, kunde in kundenliste.iteritems(): # Kundennummer-Eintrag kdnrElem = doc.createElement("kdnr") kdnrElem.appendChild(doc.createTextNode(str(kdnr))) # Namens-Eintrag nameElem = doc.createElement("name") nameElem.appendChild( doc.createTextNode( kunde.getName())) # Anschrift-Eintrag anschriftElem = doc.createElement("anschrift") anschriftElem.appendChild( doc.createTextNode( kunde.getAnschrift())) # Baue daraus einen Kunden kundeElem = doc.createElement("kunde") kundeElem.appendChild(kdnrElem) kundeElem.appendChild(nameElem) kundeElem.appendChild(anschriftElem) # Haenge den Kunden ans Dokument doc.documentElement.appendChild(kundeElem) datei = open(dateiname, "w") doc.writexml(datei, "\n", " ") datei.close() # Einen neuen Eintrag kunde = Kunde() kunde.setKdnr(5607) kunde.setName("Heike Schmidt") kunde.setAnschrift("Irgendwo") kundenliste[5607] = kunde schreibeKunden(kundenliste, "kundenliste.xml")
SAX
SAX steht für Simple API for XML. SAX geht die XML-Datei Stück für Stück durch und meldet das Eintreten von Ereignissen. Darum eignet sich SAX vor allem für sehr große XML-Dateien. Da SAX nicht die gesamte Struktur im Speicher hält, ist die Arbeitsweise grundlegend anders.Zunächst wird eine eigene Klasse KundenHandler von dem ContentHandler abgeleitet. Diese implementiert die Funktionen startElement(), endElement() und characters(). Ähnlich wie bei Callbacks der grafischen Oberflächen werden die ersten beiden Funktionen aufgerufen, wenn ein Element betreten oder verlassen wird. Die Funktion characters() dient dagegen nur zur Sammlung der Zeichen in den lokalen Variablen.
import xml.sax as sax class KundenHandler(sax.handler.ContentHandler): def __init__(self): self.kdnr = "" self.name = "" self.anschrift = "" self.aktiv = None self.typ = None def startElement(self, name, attrs): if name == "kunde": # Initialisieren eines neuen Kunden self.kdnr = "" self.name = "" self.anschrift = "" elif name == "kdnr" or name == "name" \ or name == "anschrift": self.aktiv = name def endElement(self, name): if name == "kunde": # Ein Kunde ist erfasst, Ergebnis auswerfen print(self.kdnr+"|"+self.name+"|"+self.anschrift) elif name == "kdnr" or name == "name" \ or name == "anschrift": self.aktiv = None def characters(self, content): if self.aktiv == "kdnr": self.kdnr += content elif self.aktiv == "name": self.name += content elif self.aktiv == "anschrift": self.anschrift += content def leseKunde(dateiname): handler = KundenHandler() parser = sax.make_parser() parser.setContentHandler(handler) parser.parse(dateiname) leseKunde("kundenliste.xml")Anstatt wie im DOM-Beispiel die Ergebnisse zu sammeln, werden sie hier gleich an Ort und Stelle bei endElement() auf dem Bildschirm ausgegeben. An dieser Stelle könnte auch genauso gut eine Funktion aufgerufen werden, die die Daten in einer Datenbank ablegt.