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.__anschrift
Die 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.