XML-Dateien mittels XML-Dom bearbeiten

Transcrição

XML-Dateien mittels XML-Dom bearbeiten
XML-Dateien mittels XML-Dom bearbeiten
http://www.helma-spona.de
XML-Dateien mittels XML-Dom bearbeiten
von Helma Spona
XML ist zurzeit in aller Munde. Nicht nur dass es ein wesentlicher Teil der .Net-Strategie von
Microsoft ausmacht, auch im Alltag von Office- und Datenbankentwicklern können XMLDateien sinnvoll eingesetzt werden. Eine Möglichkeit XML-Dateien zu lesen und zu schreiben
ist das XML-DOM, das hier vorgestellt werden soll.
Ausdruck
<export
format="excel5">daten.xls</export>
stellt
beispielsweise einen gültigen XML-Tag dar. Dabei
ist daten.xls der Inhalt oder Wert des Tags und
excel5 der Wert des Attributs format. XMLDateien sind hierarchisch geordnet. Das heißt
Tags können ineinander verschachtelt werden.
Gültig ist eine XML-Datei allerdings nur dann,
wenn es auf oberster Ebene nur einen Tag gibt, dem
alle anderen untergeordnet sind. Dieser oberste
Tag wird Wurzelelement oder Root-Element
genannt.
Info
Betrifft:
VBA, XML, MSXML-DOM
Systemanforderungen:
VBA-HostAnwendung + Internet Explorer 5 oder höher
Seitenzahl: 5
ANWENDUNGSMÖGLICHKEITEN
UND SYSTEMVORAUSSETZUNGEN
XML-Dateien können eingesetzt werden,
um Programmeinstellungen speichern,
Daten für die Verwendung in anderen
Anwendungen zu exportieren oder auch
Daten in die Datenbank zu importieren.
Gerade in Access spielt die Speicherung
von Programmeinstellungen in externen
Dateien zwar eine untergeordnete Rolle,
weil dazu sehr gut auch Tabellen
verwendet werden können, dennoch
macht dies in Einzelfällen Sinn, nämlich
dann, wenn die Einstellungen auch von
anderen Programmen genutzt werden
sollen.
XML ist ein W3C-Standard, genau wie das
XML DOM. Dabei handelt es sich um das
Objektmodell in dem eine XML-Datei
dargestellt werden kann. Es verfügt über
Methoden und Eigenschaften, um
Elemente der XML-Datei auszulesen oder
zu ändern. Ein XML-Tag oder ein Wert
des Tags wird im DOM als XML-NodeObjekt bezeichnet. Jedes Element der
XML-Datei, egal ob es sich um einen Tag
oder einen Textwert handelt, wird auch
als Knoten bezeichnet.
Einlesen lässt sich eine XML-Datei
natürlich auch als ganz normale Textdatei.
Allerdings ist die Ermittlung der Tags und
Attribute dann mit sehr viel Aufwand
verbunden. Besser ist da die Möglichkeit
über das XML-DOM auf die einzelnen
XML-Nodes
und
ihre
Attribute
zuzugreifen.
Um das XML-DOM zum Zugriff auf die
XML-Datei verwenden zu können, ist ein
ActiveX-Objekt erforderlich, dass der
Internet Explorer 5 und höher bereits
mitbringt, nämlich das MSXML-Objekt.
Dieses wird nachfolgend zum Lesen der
XML-Datei
eingesetzt.
Damit
das
problemlos und einfach funktioniert muss
in der Datenbank ein Verweis auf die
Objektbibliothek Microsoft XML, Version
2.0
gesetzt
werden,
der
über
Extras/Verweis in der Modulansicht oder
Entwicklungsumgebung erstellt werden
kann.
Was ist XML?
Eine XML-Datei besteht ähnlich wie HTMLDateien aus einzelnen Tags, die in spitze
Klammern eingefasst werden. Innerhalb der Tags
können Werte oder wieder andere Tags stehen.
Zudem kann ein Tag Attribute enthalten, die
innerhalb des öffnenden Tags definiert werden. Der
1
XML-Dateien mittels XML-Dom bearbeiten
http://www.helma-spona.de
Hinweis
Eventuell muss New durch CreateObject ersetzt
werden
In älteren Access-Versionen, in denen der
New-Operator noch nicht zur Verfügung
steht muss das DOMDocument-Objekt
mit den folgenden Anweisungen erzeugt
werden.
Dim xmlDoc As MSXML.DOMDocument
Set xmlDoc =
createObject("MSXML.DOMDocument")
Abbildung 1: Notwendiger Verweis
Wenn das Objekt erzeugt ist, kann die
Methode Load verwendet werden, um eine
XML-Datei zu laden. Liegt der zu lesende
Code nicht in Dateiform vor, kann dieser
Code ebenfalls eingelesen werden, nämlich
über die LoadXML-Methode. Kann der
Code bzw. die Datei eingelesen werden,
wird dadurch das DOMDocument-Objekt
gefüllt. Sollte der einzulesende Text oder
die Datei keinen gültigen XML-Code
enthalten, tritt allerdings beim Einlesen
des Codes ein Laufzeitfehler auf.
EINE XML-DATEI LESEN
Da eine XML-Datei einen hierarchischen
Aufbau hat und nach dem Einlesen der
Datei nicht auf Anhieb zu ermitteln ist,
wie viele untergeordnete Knoten der
Wurzelknoten hat und wie tief die
Verschachtelung ist, lässt sich eine XMLDatei nur rekursiv auslesen.
Für jeden Knoten kann dazu über die
hasChildNodes-Methode
ermittelt
werden, ob er untergeordnete Knoten hat.
Wenn ein Knoten erreicht ist, der keine
untergeordneten Knoten hat, wird die
Prozedur einfach nicht mehr aufgerufen.
Das Wurzelelement kann nach dem
Einlesen über die documentElementMethode ermittelt werden, dieses wird
dann an die Prozedur ZweigLesen
übergeben. Sie durchsucht die Datei
rekursiv und gibt Namen und Werte der
Knoten im Direktfenster aus.
Bevor die Knoten rekursiv durchlaufen
werden können, muss aber zunächst
einmal das XML-DOM-Objekt erzeugt
und die Datei geladen werden. Das erledigt
im Listing 1 die Prozedur XMLLesen. Sie
definiert
zunächst
die
benötigten
Variablen xmlRoot und xmlDoc. Die
Variable xmlRoot soll das Wurzelelement
der Datei speichern und muss daher den
Typ MSXML.IXMLDOMNode haben.
Dieses Objekt stellt ein allgemeines NodeObjekt dar, dem auch speziellere NodeObjekte zugewiesen werden können. Dies
können bspw. XML-Elemente wie normale
Tags, Textelemente wie die Inhalte von
Tags, Kommentare oder auch XMLAttribute sein. Die Variable xmlDoc stellt
das
XML-DOM
dar.
Mit
dem
Schlüsselwort New wird es zunächst als
leeres DOM erzeugt.
Listing 1: Einlesen einer XML-Datei
Const strPFAD = "C:\TreeView_XML_0402\"
Sub XMLLesen()
Dim strFilename As String
Dim xmlRoot As MSXML.IXMLDOMNode
Dim xmlDoc As New MSXML.DOMDocument
strFilename = "buecher.xml"
xmlDoc.Load (strPFAD & strFilename)
Set xmlRoot = xmlDoc.documentElement()
ZweigLesen xmlRoot
Set xmlRoot = Nothing
Set xmlDoc = Nothing
End Sub
Wie jede rekursive Prozedur besteht auch
die hier verwendete aus zwei Teilen. Der
2
XML-Dateien mittels XML-Dom bearbeiten
http://www.helma-spona.de
eine wird ausgeführt, wenn der aktuelle
XML-Knoten
den
Knotentyp
NODE_ELEMENT hat. In diesem Fall
handelt es sich um einen XML-Tag und
nicht nur um einen Kommentar oder den
Inhalt eines Tags.
Listing 2: Rekursives lesen der XMLKnoten
Sub ZweigLesen(xmlNode As MSXML.IXMLDOMNode)
Dim xmlTmp As MSXML.IXMLDOMNode
Dim xmlTmpElem As MSXML.IXMLDOMElement
Hinweis
If xmlNode.nodeType = NODE_ELEMENT Then
Ob diese Prüfung sinnvoll ist, hängt vom Zweck
der Prozedur ab. Wenn nur Knotenname und
Werte ausgegeben werden sollen, ist dies
notwendig. Im Beitrag zum TreeViewSteuerelement wird eine Abwandlung der
Prozedur verwendet, wo eine andere Bedingung
definiert ist. In jedem Fall muss der erste Teil der
rekursiven Prozedur unabhängig davon ausgeführt
werden, ob es untergeordnete Elemente gibt. Wenn
im ersten Teil der Prozedur die Prozedur wieder
aufgerufen wird, erfolgt die Ausgabe von unten
nach oben, das heißt das letzte Element wird zuerst
ausgegeben.
Set xmlTmpElem = xmlNode
If xmlTmpElem.baseName <> _
xmlNode.ownerDocument. _
documentElement.baseName Then
Debug.Print xmlTmpElem.nodeName;
If xmlTmpElem.hasChildNodes Then
If xmlTmpElem.firstChild.nodeType = _
NODE_TEXT Then
Debug.Print ":" & _
xmlTmpElem.firstChild.nodeValue
Else
Debug.Print ""
End If
Else
Debug.Print ""
Handelt es sich um einen XML-Tag, wird
geprüft ob es das Wurzelelement ist.
Wenn nicht wird der Name des Elements
mit
der
nodeName-Eigenschaft
ausgegeben. Als nächstes wird dann
geprüft, ob es für den XML-Tag einen
Inhalt gibt. Auch Textinhalte werden als
Node-Objekte behandelt. Das heißt, nur
wenn die Methode hasChildNodes den
Wert true liefert kann das Element einen
Text haben. Dies ist in der Regel das erste
untergeordnete Element, das mit der
firstChild-Eigenschaft ermittelt werden
kann. Handelt es sich dabei um ein
Textelement, gibt die Eigenschaft die
Konstante NODE_TEXT zurück. In
diesem Fall soll der Wert des ersten
untergeordneten
XML-Elements
ausgegeben werden, wenn nicht, eine leere
Zeichenkette. Im zweiten Teil der
Prozedur wird nun geprüft, ob das als
Parameter übergebene XML-Element
untergeordnete Knoten hat. Wenn ja, wird
die ChildNodes-Auflistung in einer
Schleife durchlaufen und die Prozedur für
jeden untergeordneten Knoten erneut
aufgerufen.
End If
End If
End If
If xmlNode.hasChildNodes = True Then
For Each xmlTmp In xmlNode.childNodes()
ZweigLesen xmlTmp
Next xmlTmp
End If
End Sub
XML-DATEIEN ÄNDERN
Das XML-DOM kann allerdings nicht nur
zum Lesen der XML-Dateien verwendet
werden, es stellt auch eine Save-Methode
zur Verfügung mit der die XML-Datei
gespeichert werden kann. Diese kann
zudem vorher geändert und ergänzt
werden. Die Prozeduren in Listing 3
zeigen, wie ein Datensatz an eine
bestehende XML-Datei angehängt werden
kann. Die hier verwendete Datei zeigt
Abbildung 2.
3
XML-Dateien mittels XML-Dom bearbeiten
http://www.helma-spona.de
freischwebend im XML-DOM. Mit der
AppendChild-Methode kann es dann an
das übergeordnete Element angehängt
werden. Für die einzelnen Tags <titel>,
<autor> etc. ist dies das <buch>-Element.
Das <buch>-Element wird hingegen an das
Wurzelelement der Datei angehängt.
Bevor das jedoch passiert wird noch das
Attribut erzeugt. Attribute können analog
zur Methode createElement mit der
createAttribute-Methode erzeugt werden.
Deren Wert lässt sich dann über die
Eigenschaft Value bestimmen. Um das
Attribut dem XML-Tag hinzuzufügen,
wird es der setNamedItem-Methode
übergeben. Mit Aufruf der Save-Methode
wird die Datei dann gespeichert.
Abbildung 2: Die verwendete XML-Datei
Jeder Datensatz wird dabei durch einen
<buch>-Knoten dargestellt, für den über
das id-Attribut ein eindeutiger Schlüssel
definiert ist. Um einen neuen Datensatz
anzufügen, muss also zunächst der letzte
Schlüssel ermittelt und der nächste
berechnet werden. Dazu wird die Datei
eingelesen und wieder das Wurzelelement
ermittelt. Hat es untergeordnete Elemente,
sind Datensätze vorhanden. In diesem Fall
wird über die lastChild-Eigenschaft das
letzte Element zurückgegeben und es wird
geprüft, ob dieses Element Attribute hat.
Die Attribute werden in der AttributsAuflistung verwaltet, deren lengthEigenschaft ihre Anzahl angibt. Enthält
der XML-Tag Attribute, wird mit der
Methoode getNamedItem das id-Attribut
zurückgegeben und dessen Wert über die
nodeValue-Eigenschaft in der Variablen
strTmp gespeichert. Wenn es kein idAttribut gibt, kommt es dabei jedoch zu
einem Laufzeitfehler, der mit On Error
Resume Next ignoriert wird. In diesem
Fall wird als Nummer der Wert 1 ermittelt.
Gibt es das Attribut wird der Wert nach
dem Zeichen # ermittelt in eine Zahl
konvertiert und 1 dazu addiert. Dies ist
dann die Nummer für den neuen
Datensatz.
Listing 3: Hinzufügen von Datensätzen
zur XML-Datei
Sub Aufruf()
DatensatzAnfuegen strPFAD & _
"buecher.xml", "3815821762", _
"Helma Spona", _
"Das Große Buch Access 2002 Programmierung", _
"40,88"
End Sub
Sub DatensatzAnfuegen(strDatei As String, _
strISBN As String, _
strAutor As String, strTitel As String, strPreis As String)
'ID ermitteln
Dim xmlRoot As MSXML.IXMLDOMNode
Dim xmlNode As MSXML.IXMLDOMNode
Dim xmlDoc As New MSXML.DOMDocument
Dim xmlElem As MSXML.IXMLDOMElement
Dim xmlElem2 As MSXML.IXMLDOMElement
Dim xmlAttr As MSXML.IXMLDOMAttribute
Dim lngID As Long
Dim bytPos As Byte
Dim strTmp As String
Anschließend wird der neue Datensatz
eingefügt. Dazu muss zunächst das
übergeordnete <buch>-Element erzeugt
werden. Das geschieht, wie auch bei den
untergeordneten Elementen über die
CreateElement-Methode
des
DOMDocument-Objekts. Das so erzeugt
Element existiert nun zwar, ist jedoch
noch nicht in die Knotenhierarchie
eingeordnet, sondern existiert quasi
xmlDoc.Load (strDatei)
Set xmlRoot = xmlDoc.documentElement
If xmlRoot.hasChildNodes Then
Set xmlNode = xmlRoot.lastChild()
If xmlNode.Attributes.length > 0 Then
On Error Resume Next
4
XML-Dateien mittels XML-Dom bearbeiten
http://www.helma-spona.de
vor allem bei großen Dateien, weil der
Zugriff auf die Knoten einer XML-Datei
wesentlich schneller ist, das ein Textdatei
sequentiell zu lesen und die Werte über
entsprechenden String-Funktionen zu
ermitteln. Vor allem wenn XML-Dateien
zur
Speicherung
von
Programmeinstellungen verwendet wird,
lassen sich auch sehr gut im TreeViewSteuerelement
als
Baumstruktur
darstellen, wie im Beitrag zu diesem
Steuerelement noch gezeigt wird. <HS>
strTmp = _
xmlNode.Attributes.getNamedItem("id" _
).nodeValue
bytPos = InStr(1, strTmp, "#", vbTextCompare)
If bytPos > 0 Then
strTmp = Mid(strTmp, bytPos + 1)
End If
If strTmp <> "" Then
lngID = Val(strTmp) + 1
Else
lngID = 1
End If
End If
Rechtliche Rahmenbedingungen
Else
Alle Inhalte wurden nach bestem Wissen und
Gewissen zusammengestellt. Ich übernehme für
Fehlerfreiheit allerdings keine Gewähr und hafte
keinesfalls für Folgen, die sich aus Fehlern oder
unsachgemäßem Gebrauch des Codes und der
Inhalte ergeben.
Code und Beispiele dürfen für den privaten
Gebrauch frei verwendet werden. Eine
Veröffentlichung (auch auszugsweise) sowohl
online wie auch auf klassischen Medien ist nur
nach meiner ausdrücklichen Zustimmung möglich.
Verlinkung der Artikel ist jedoch erlaubt und
erwünscht, solange immer auf die Webseite und
nicht die PDF-Datei verlinkt wird.
lngID = 1
End If
'Datensatz anfügen
Set xmlElem = xmlDoc.createElement("buch")
Set xmlElem2 = xmlDoc.createElement("ISBN")
xmlElem2.Text = strISBN
xmlElem.appendChild xmlElem2
Set xmlElem2 = xmlDoc.createElement("titel")
xmlElem2.Text = strTitel
xmlElem.appendChild xmlElem2
Set xmlElem2 = xmlDoc.createElement("autor")
xmlElem2.Text = strAutor
xmlElem.appendChild xmlElem2
Set xmlElem2 = xmlDoc.createElement("preis")
xmlElem2.Text = strPreis
xmlElem.appendChild xmlElem2
'id-Attribut hinzufügen
Set xmlAttr = xmlDoc.createAttribute("id")
xmlAttr.Value = "buch#" & lngID
xmlElem.Attributes.setNamedItem xmlAttr
xmlRoot.appendChild xmlElem
'Datei speichern
xmlDoc.Save strDatei
Set xmlNode = Nothing
Set xmlRoot = Nothing
Set xmlDoc = Nothing
End Sub
FAZIT
Schreiben und lesen von XML-Dateien ist
mit Hilfe des passenden MSXML-DomObjekts gar nicht schwer und eignet sich
5

Documentos relacionados