This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Die Lernen-Reihe In der Lernen-Reihe des Addison-Wesley Verlages sind die folgenden Titel bereits erschienen bzw. in Vorbereitung: André Willms C-Programmierung lernen 432 Seiten, ISBN 3-8273-1405-4 André Willms C++-Programmierung lernen 408 Seiten, ISBN 3-8273-1342-2 Guido Lang, Andreas Bohne Delphi 5 lernen 432 Seiten, ISBN 3-8273-1571-9 Walter Herglotz HTML lernen 323 Seiten, ISBN 3-8273-1717-7 Judy Bishop Java lernen 636 Seiten, ISBN 3-8273-1794-0 R. Allen Wyke, Donald B. Thomas Perl 5 lernen ca. 400 Seiten, ISBN 3-8273-1650-2 Michael Ebner SQL lernen 336 Seiten, ISBN 3-8273-1515-8 René Martin VBA mit Word 2002 lernen 393 Seiten, ISBN 3-8273-1897-1 René Martin VBA mit Office 2000 lernen 576 Seiten, ISBN 3-8273-1549-2 Patrizia Sabrina Prudenzi VBA mit Excel 2000 lernen 512 Seiten, ISBN 3-8273-1572-7 Dirk Abels Visual Basic 6 lernen 425 Seiten, ISBN 3-8273-1371-6
Sandini Bib
René Martin
XML und VBA lernen Anfangen, anwenden, verstehen
An imprint of Pearson Education München • Boston • San Francisco • Harlow, England Don Mills, Ontario • Sydney • Mexico City Madrid • Amsterdam
Sandini Bib
Die Deutsche Bibliothek – CIP-Einheitsaufnahme Ein Titeldatensatz für diese Publikation ist bei Der Deutschen Bibliothek erhältlich.
Warum XML? 9 Warum ein weiteres Buch zu XML? 10 Wie ist dieses Buch aufgebaut? 11 Voraussetzungen 12 Schreibkonventionen und verwendete Symbole 12 Gültige XML-Dokumente 15 XML-Grundlagen 15 Weitere Tags 20 Der Zeichensatz 23 Abkürzungen 25 CDATA-Abschnitte 25 Kommentare 26 Attribute 29 Zusammenfassung 36 Fragen zu Kapitel I 37 Wohlgeformte XML-Dokumente 41 Die DTP 41 Die Regeln in der DTP 41 Wohlgeformt und gültig 43 Weiter Tags 52 Kommentare 55 Attribute 56 Die DTD auslagern 64 Abkürzungen 66 Teile des Dokumnets auslagern 69 Namensräume 71
5
Sandini Bib
2.11 2.12 2.13 2.14 2.15 3 3.1 3.2 3.3 3.4 3.5
Validierung 83 XML-Editoren 84 Zusammenfassung 88 Fragen zu Kapitel II 88 XML, HTML und XSL - die Ausgabe 91 Style Sheets 91 Einbetten von XML-Daten in ein HTML-Dokument 104 XSL 109 Zusammenfassung 135 Fragen zu Kapitel III 136
4 4.1 4.2 4.3
Daten in Excel und Access aufbereiten 141 Excel 141 Access 149
Excel 161 Zugriff auf Access 175 Übungen zu Kapitel V 192 XML-Dokumente mit VBA erzeugen 195 Von Excel mit VBA nach XML und zurück 195 Durchlaufen von mehreren Ebenen 213 Zwischenergebnis 216 Weitere XML-Elemente in VBA 221 Von Access mit VBA nach XML und zurück 241 Das Tree View-Steuerelement von VB 245 Zusammenfassung 258 Fragen zu Kapitel VI 258 Transformationen von XML mit dem DOM 263 Excel 268 Access 274 Zusammenfassung 280 Fragen zu Kapitel VII 280
Inhaltsverzeichnis
Sandini Bib
8 8.1 8.2 8.3 8.4
XML-und XSl-Dateien mit VBA erzeugen 285
9 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8
Lösungen 307
Excel 285 Access 296 Zusammenfassung 303 Fragen zu Kapitel VIII 304
Kapitel I 307 Kapitel II 307 Kapitel III 310 Kapitel IV 311 Kapitel V 318 Kapitel VI 322 Kapitel VII 324 Kapitel VIII 325
A A.1 A.2 A.3 A.4
Ausblick 327
S
Stichwortverzeichnis 331
XML-Dokumente 327 Darstellung von XML 327 Excell, Access und VBA 328 Weitere Einsatzbereiche von XML 328
Inhaltsverzeichnis
7
Sandini Bib
Sandini Bib
V
Vorwort
V.1
Warum XML?
le
n e rn
In den siebziger Jahren hatten wir Datenbanksysteme (IMS von IBM), die hierarchisch organisiert waren. Es stellte sich bald heraus, dass sie unpraktisch waren und die Wirklichkeit nur zu einem geringen Teil beschrieben. Durch die Entwicklung des relationalen Datenbankmodells durch E. F. Codd wurden sie weitgehend zurückgedrängt. Mit XML wird nun wieder ein hierarchisches Datenbankmodell eingeführt. In den achtziger Jahren wurden in den ersten Textverarbeitungsprogrammen die Formatierungsbefehle in spitzen Klammern eingefügt. Das sah damals folgendermaßen aus: Was Sie schon immer fragen wollten.
In den neunziger Jahren, im Zeitalter von WYSIWYG, lächelten wir darüber. Und mit XML sollen die spitzen Klammern nun wieder kommen. Muss das sein? Solche und ähnliche Fragen höre ich häufig, wenn die Diskussion auf XML zu sprechen kommt. Ich denke, dass dabei einiges durcheinander gebracht wird. Von einer Oberflächenstruktur kann nicht auf eine Tiefenstruktur geschlossen werden. Und: XML will keine Datenbanksysteme ersetzen. Es ist sicherlich nicht das Ziel von XML, das relationale Datenbankmodell abzulösen. Das Ziel und das Konzept ist ein anderes: Es geht um ein universelles Datenbankformat (oder noch allgemeiner: Textformat), in welchem Daten gespeichert werden können. Jedes System sollte diese Daten lesen und weiterverarbeiten können. Damit könnten aus einer beliebigen Datenbank (oder Textverarbeitung) die Daten in ein XML-Dokument ausgegeben werden. Dort können die Daten aufbereitet, das heißt transformiert werden und in einem anderen System (Handy, Browser, Belichter, ...) angezeigt und gedruckt werden.
:DUXP ;0/"
9
Sandini Bib
XML will auch nicht HTML ersetzen. In den letzten Jahren hat sich herausgestellt, dass HTML einige Schwächen aufweist. Die größte Schwäche ist das Vermengen von Daten und Formatierungsanweisungen. Zwar wurde mit den Cascading Style Sheets (CSS) die Möglichkeit geschaffen, Formatierungsanweisungen an einer Stelle zentral abzulegen, so wie es in Satz- und Textverarbeitungsprogrammen schon lange Usus ist. Darüber hinaus wurden mit den beiden Tags ',9! und 63$1! zwei „freie“ Tags geschaffen, an die Formatierungen gebunden werden können. Dennoch: ganz befriedigend sind beide Lösungsansätze nicht. Diese Lücke will XML schließen.
V.2
Warum ein weiteres Buch zu XML?
Es gibt bereits eine große Anzahl an XML-Büchern. Warum ein weiteres? Beim Lesen der Bücher sind mir mehrere Dinge aufgefallen. Einige Bücher waren sehr knapp. Ich habe mich immer gefragt, wer das versteht. Mir fehlten oft Beispiele, Erläuterungen, Begründungen. Oft kam mir das Lesen eines XML-Buchs vor wie das Lesen eines Mathematikbuchs, bei dem die Sätze, Lemmata und Theoreme untereinander aufgelistet sind. Zum Zweiten ist mir aufgefallen, dass XML-Dokumente als Text in den Büchern stehen. Das ist korrekt und schön zu lesen – aber nur wenige Autoren beantworten die Frage, ob man diesen Text denn abtippen muss. Woher kommen die Daten eigentlich? Werden sie so in ein Dokument eingegeben und dort geändert? Und: Was mache ich mit einer Datenbank, in der die Daten schon vorliegen? Muss ich sie etwa noch einmal abtippen? Auch eine große Datenmenge? Natürlich wird die Antwort auf diese Fragen indirekt gegeben. Die Lösungen heißen SAX und DOM. Und damit sind wir beim dritten Knackpunkt vieler Bücher. Java gewinnt eine enorme Bedeutung durch XML. Java ist wirklich eine prima Programmiersprache – sie ist leistungsfähig, gut durchdacht und verarbeitet XML-Daten hervorragend. Nur: Kann man das nicht auch mit Microsoft-Produkten? Nicht dass ich ein höriger Anhänger, ein gekauftes Mitglied oder ein blinder Jünger dieses Konzerns und seiner Produkte wäre, aber: Auf den meisten Rechnern in unserem Land (und dies gilt auch für die Schweiz und für Österreich) laufen Microsoft-Produkte. Viele Firmen setzen Excel und Access ein, um ihre Daten zu halten, zu pflegen und auszuwerten. Aber in keinem Buch wird ausführlich und anhand von Beispielen erklärt, wie man diese Daten in ein XML-Dokument transferiert. Excel und Access besitzen nun schon seit vielen Jahren die mächtige Programmiersprache VBA, mit der man auf Daten zugreifen kann.
10
9RUZRUW
Sandini Bib
Office verfügt seit der Version 10.0 (oder Office 2002, Office XP) über die Möglichkeit, Daten direkt als XML-Dokument zu speichern. So, wie man seit vielen Jahre Daten in ein allgemeines Textformat speichern kann. Nur: Das Ergebnis, das Excel liefert, sieht, vorsichtig ausgedrückt, grauenvoll aus. Die Daten müssen transformiert werden, das heißt von überflüssigen Informationen befreit werden. Wie das automatisiert werden kann, wird im vorliegenden Buch beschrieben.
V.3
Wie ist dieses Buch aufgebaut?
Im ersten Teil des Buchs geht es um das zentrale Thema XML – wie die Daten strukturiert sind, was dabei zu beachten wäre, wenn man die Daten per Hand eingeben würde (was sicherlich nur wenige machen). Es geht um den korrekten Aufbau und die Syntax eines XML-Dokuments. Danach werden DTD und Schema beschrieben. Mit ihrer Hilfe kann sichergestellt werden, dass ein XML-Dokument bestimmten Regeln folgt, dass es bestimmte Elemente besitzt oder nicht besitzt, dass Attribute nur aus einem bestimmten Wertebereich stammen und so weiter. Was diese beiden Elemente leisten, wird im zweiten Kapitel beschrieben. Im dritten Teil des Buchs geht es um die Veröffentlichung im Internet. Um den Zusammenhang von XML und HTML, um die vernünftige Darstellung im Browser, aber auch um Filter- und Sortiermöglichkeiten innerhalb eines Browsers. Zwei Techniken stehen hier im Zentrum: XSL und CSS. Und damit sind die Vorarbeiten geleistet, um eine Datenbank im Internet zu veröffentlichen. Es würde den Rahmen des Buches sprengen, wenn nun noch über sichere Verbindungen, CGI, ASP und andere Internet-Datenbanksprachen gesprochen werden würde. Ebenso wurde das weit reichende Kapitel JavaScript ausgeklammert. Auch damit könnte man dynamisch bestimmte Daten suchen oder filtern. Wer mit seinem Wissen bis zu diesem Punkt gekommen ist, der muss weitere Literatur zu Rate ziehen, in der diese Dinge abgehandelt werden. Anschließend geht es um die beiden Anwendungen Excel und Access: Wie kann man Daten dort aufbereiten, dass sie die Form haben, aus der sie exportiert werden können? Ich erlebe in meinen Schulungen immer wieder Teilnehmer, die mit Kopieren und Einfügen in eine Datenbank Informationen duplizieren. Oder die den Assistenten „Text in Spalten“ in Excel nicht kennen. Einige dieser Hilfsmittel der beiden Programme kommen zu Wort. An einem Beispiel wird gezeigt, wie Fremddaten importiert und danach aufbereitet werden. Nachdem der VBA-Zugriff auf Excel und Access erklärt wurde, wird gezeigt, wie man mit Hilfe dieser Programmiersprache die Daten ausliest und in ein XML-Dokument verwandelt. Schön und gut, mag jetzt der
:LH LVW GLHVHV %XFK DXIJHEDXW"
11
Sandini Bib
eine oder andere sagen, aber mein Excel 2002 und mein Access XP können das doch alleine. Das ist richtig: Nur stehen die Daten dann möglicherweise nicht in der Form zur Verfügung, wie sie benötigt werden. Abhilfe schafft eben das Objektmodell von DOM. Sie sehen schon: Das ist ein weites Feld, das wir vor uns haben. Und das empfinde ich auch als spannend: Mit XML werden eine Reihe bekannter Technologien miteinander verknüpft. Anwendungsgebiete sind mannigfaltig.
V.4
Voraussetzungen
Um die beschriebenen Beispiele nachvollziehen zu können, benötigen Sie den Internet Explorer 5.0 oder höher von Microsoft. Für die Erstellung eines XML-Dokuments„per Hand“, wie es in den Kapiteln eins bis drei beschrieben wird, genügt ein einfacher Editor, wie beispielsweise der von Windows mitgelieferte. Sie können aber auch Word oder jedes andere Textverarbeitungssystem verwenden. Für die übrigen Kapitel sind Excel und Access in den Versionen 97, 2000 oder 2002 / XP (oder in der Microsoft-Zählweise: 8.0, 9.0 oder 10.0) nötig. Da es im Zentrum um VBA geht, genügen Excel und Access. Sollten Sie über Visual Basic verfügen, so können Sie auch damit arbeiten. Access 97 verfügt noch nicht über einen Zugriff auf ADO. Um die hier beschriebenen Beispiele nachvollziehen zu können, müssen Sie die Kenntnisse von DAO besitzen. Ebenso beinhaltet Excel 97 das XML-DOM in der Version 1.0. Erst ab der Version 2.0 (Excel 2000) steht das vollständige und leistungsfähige Tool zur Verfügung, wie es hier beschrieben ist.
V.5
Schreibkonventionen und verwendete Symbole
Folgende Tabelle gibt eine Übersicht zu den im Buch verwendeten Schreibkonventionen: Menüs
DATEI | DRUCKEN
Kapitälchen
HTML- und XML-Code, VBA-Anweisungen
Sub Los_Gehts
Nichtproportionalschrift
spezielle Bezeichnungen, die im Text mitgelesen werden können
„True“
in „Anführungszeichen“
Tasten
(Strg)+(A)
als Tasten
Optionale, das heißt nicht notwendige Programmzeilen
[Anweisung 2]
[in eckigen Klammern]
„Laufweite“
Tabelle V.1: Die Schreibkonventionen in diesem Buch
12
9RUZRUW
Sandini Bib
Um Ihnen die Orientierung in diesem Buch zu erleichtern, haben wir den Text in bestimmte Funktionsabschnitte gegliedert und diese durch entsprechende Symbole oder Icons gekennzeichnet. Folgende Icons finden Verwendung: Beispiele helfen Ihnen, sich schneller und sicher im Feld der VBAProgrammierung und der XML-Dokumente zu orientieren. Sie werden darum mit diesem Icon gekennzeichnet. Manches ist von besonderer Bedeutung und verdient darum auch, besonders hervorgehoben zu werden. Solche Hinweise sind sehr nützlich, damit erreichen Sie schneller das Ziel. Manches geht ganz leicht. Wenn man nur weiß wie. Tipps und Tricks finden Sie in den Abschnitten, in denen dieses Icon steht. Achtung, mit diesem Icon wird eine Warnung angezeigt. An der markierten Stelle sollten Sie besonders achtsam sein. Übungen finden Sie am Ende jedes Kapitels. Sie helfen, den gelesenen Stoff selbstständig zu wiederholen und zu vertiefen. Nun bleibt mir nur noch, Ihnen viel Spaß beim Lesen des Buchs zu wünschen.
René Martin im Januar 2002
6FKUHLENRQYHQWLRQHQ XQG YHUZHQGHWH 6\PEROH
13
Sandini Bib
Sandini Bib
le
1
Gültige XML-Dokumente
n e rn
In díesem Kapitel geht es um die Grundlagen von XML. Dabei soll gezeigt werden, wie ein XML-Dokument aussehen darf, welche Regeln hierfür zu beachten sind und wo Fehlerquellen liegen.
1.1
XML-Grundlagen
Um dies nachvollziehen zu können, genügt ein einfacher Editor, in dem die Daten eingegeben werden. Um das Ergebnis darzustellen, sollten Sie einen Browser installiert haben, am besten den Internet Explorer von Microsoft, da dieser schon in der Version 5.0 XML unterstützt hat. Das einfachste XML-Dokument hat die Form: Wofuer sie besonders schwaermet, wenn es wieder aufgewaermet.
erstellt von ciando
Speichern Sie es unter einem beliebigen Namen und geben Sie der Datei die Endung „xml“. Also nennen Sie es beispielsweise „bsp01.xml“. Dies kann man im Internet Explorer sichtbar machen:
Abbildung 1.1: Das erste (und einfachste) XML-Dokument
;0/*UXQGODJHQ
15
Sandini Bib
Vielleicht sind Sie erstaunt, dass dies ein XML-Dokument ist. Vielleicht haben Sie es sich anders vorgestellt. Die spitzen Klammern mit dem Begriff, den sie umschließen, sind Ihnen sicherlich von HTML bekannt – sie heißen „Tag“. Der Internet Explorer stellt die spitzen Klammern, die den Namen des Tags („Bolte“) einleiten und abschließen, in blauer Farbe dar. Der TagName selbst erscheint braun. Und die Daten, um die es geht, in schwarzer Farbe. Wem dies nicht genügt, der muss sich noch ein bisschen gedulden. Schließlich sehen auch die „nackten“ Daten in Excel oder in einer Access-Tabelle nicht ansprechend aus. In Excel kann man sie formatieren; in Access stehen zur Ausgabe Berichte zur Verfügung. Doch dazu mehr in Kapitel 3. Um dies noch einmal deutlich zu formulieren: Auch XML hält und strukturiert die Daten nur. XML ist nicht für die Darstellung, für das Aussehen oder die Ausgabe zuständig. Das erledigen andere (zum Beispiel XSL oder CSS) – und dies wird noch beschrieben. Doch das XML-Dokument soll etwas genauer dargestellt werden. In der ersten Zeile muss die Versionsnummer oder genauer: die XML-Deklaration – stehen: Spitz, das war ihr erstes Wort.
Mit xml version="1.0" wird die Versionsnummer festgelegt, die beiden Fragezeichen leiten den Tag ein und schließen ihn wieder. Diese Trennzeichen weisen auf eine PI (Processing Instruction) hin – in diesem Fall auf die verwendete XML-Version. Manchmal werden die Tags in der Literatur als das Markup des Dokuments, als Prolog oder als XML-Deklaration bezeichnet: ist das Markup, während „Spitz, das war ihr erstes Wort.“ Text (oder allgemeiner CDATA) darstellt. Doch dazu gleich noch mehr. „xml“ in der XML-Deklaration muss in Kleinbuchstaben geschrieben werden. Zurzeit darf dort nur die Version 1.0 stehen. Einige Browser, wie beispielsweise der Internet Explorer, zeigen ein XML-Dokument auch ohne Versionsnummer an. Korrekt ist es allerdings nicht. Der öffnende und schließende -Tag erinnert an HTML und ist sicherlich von dort bekannt: Witwe Bolte.
Allerdings weisen HTML-Tags einige Unterschiede zur ersten Zeile in XML auf:
16
*OWLJH ;0/'RNXPHQWH
Sandini Bib
• Der Name (oder Bezeichner) des Tags () ist frei definiert. Er muss mit einem Buchstaben oder einem Unterstrich beginnen und darf nur gültige Zeichen („name characters“) enthalten. Das sind Buchstaben, Ziffern und Unterstriche. Umlaute, das „ß“, Satz- und Sonderzeichen sind verboten. Eigentlich sind auch Doppelpunkte zugelassen; allerdings ist der Doppelpunkt für einen Namensraum reserviert. Deshalb sollten Sie Doppelpunkte vermeiden. Die ersten drei Buchstaben dürfen nicht „xml“ oder „XML“ sein – sie sind reserviert. Ein gültiger Name ist folglich Busch Wilhelm_Busch WBUSCH1877
Falsch dagegen W.Busch *Busch* Wilhelm Busch
Abbildung 1.2: Das Tag enthält einen ungültigen Namen: „Witwe Bolte“.
• Tags umschließen immer Elemente. Ein Element besteht immer aus einem Start-Tag und einem End-Tag. End-Tags werden durch einen Schrägstrich gekennzeichnet: . Elemente können andere Elemente, Zeichen („character data“), Zeichenreferenzen, EntityReferenzen, Verarbeitungsanweisungen (PIs), Kommentare oder CDATA-Abschnitte enthalten. Sie werden im Einzelnen noch erläutert.
;0/*UXQGODJHQ
17
Sandini Bib
• Groß- und Kleinschreibung spielt eine Rolle. Der Anfangstag wird mit , mit und wird mit beendet. Alles andere führt zu Fehlern. Am besten ist es, wenn Sie sich auf eine Schreibweise festlegen und diese konsequent durchhalten. Der Grund liegt in der Internationalisierung von XML: Die meisten romanischen Sprachen teilen ihr Alphabet nicht streng in Großund Kleinbuchstaben auf (mein Vorname wird im Spanischen RENÉ geschrieben, im Französischen RENE), das griechische Sigma hat zwei Varianten für den Kleinbuchstaben und im Arabischen unterscheiden sich die Buchstaben nach der Position im Wort (Anfang, Mitte, Ende – von links oder rechts verbunden oder unverbunden) und nicht nach Groß- und Kleinschreibung. Aufgrund von Problemen, die daraus entstehen könnten, wird in XML zwischen Großund Kleinschreibung unterschieden.
Abbildung 1.3: Groß- und Kleinschreibung muss eingehalten werden!
• Jedes Anfangstag hat ein Schlusstag. Anders als HTML, wo
, oder nicht beendet werden müssen, muss in XML jedes Tag ein Pendant am Ende besitzen. Eine Ausnahme bildet die erste Zeile (oder allgemein: jede Processing Instruction):
18
*OWLJH ;0/'RNXPHQWH
Sandini Bib
Abbildung 1.4: Vergessen Sie nicht das schließende Tag!
• Dies sind die ersten Eigenschaften – weitere folgen sogleich. • Um zu überprüfen, ob Sie Ihr Dokument korrekt erstellt haben, können Sie es im Internet Explorer öffnen. Entweder per Doppelklick auf den Dateinamen im Explorer oder direkt im Explorer über das Menü DATEI / ÖFFNEN.
Abbildung 1.5a: Die Datei in einem Editor
;0/*UXQGODJHQ
19
Sandini Bib
Abbildung 1.5b: Die Datei im Browser
Beachten Sie die unterschiedlichen Farben auf Ihrem Bildschirm: In Blau werden Verarbeitungsanweisungen dargestellt, in Braun stehen die Namen der Tags und in Schwarz die Texte.
1.2
Weitere Tags
Es wäre langweilig, wenn Datenbanken nur aus einem Datensatz oder wenn Bücher nur aus einem Absatz bestehen würden. Es ist gar keine Frage, dass das Dokument mehrere Tags, das heißt mehrere Elemente, beinhalten kann. Aber für sie gibt es Regeln. Die fromme Helene
Angenommen, in einem Dokument existieren die beiden Tags „Buch“ und „Titel“. Es fällt auf, dass „Buch“ als Element „Titel“ besitzt, „Titel“ dagegen Daten enthält. Man nennt das unterste Tag „Wurzelelement“ (Root Element), alle weiteren, die darauf aufbauen, sind die Unter-Tags oder Kindelemente. Damit ergibt sich eine weitere Regel für die Tags: • Tags sind hierarchisch strukturiert. • Untergeordnete Tags sind immer vollständig in übergeordneten eingeschlossen. Beispielsweise so: Die fromme Helene
20
*OWLJH ;0/'RNXPHQWH
Sandini Bib
• Niemals so: Fips, der Affe
Abbildung 1.6: Tags müssen korrekt verschachtelt sein
Die letztere Variante mag in HTML funktionieren, in XML nicht. • Jedes XML-Dokument darf nur ein Wurzelelement besitzen. • Jedes XML-Dokument besitzt ein Wurzelelement • Zwar könnte man sich ein leeres Dokument vorstellen oder ein XMLDokument, das nur aus Processing Instructions oder nur aus Kommentaren besteht, aber gemäß der XML-Definition ist es dann kein gültiges XML-Dokument. Die Einrückungen werden im Buch wegen der besseren Lesbarkeit vorgenommen. Sie müssen nicht sein – man müsste noch nicht einmal eine neue Zeile beginnen, sondern könnte die gesamten Elemente in einer Zeile schreiben (was die Lesbarkeit und Erlernbarkeit nicht gerade erhöht). Alle Leerräume zwischen Tags und Werten von Attributen werden von Parsern nicht beachtet. Dazu gehören Leerzeichen, Absatzzeichen (CR), Zeilenvorschub (LF) und Tabulatoren. Das bedeutet, dass folgendes Dokument: Die fromme Helene
äquivalent ist mit dem nächsten:
:HLWHUH 7DJV
21
Sandini Bib Die fromme Helene
Ein Element kann auch leer sein. Dann kann einfach kein Text zwischen den Tags stehen:
oder es kann in einem Tag geschrieben werden, welches durch einen Schrägstrich am Ende gekennzeichnet wird:
Und mit diesem Wissen können nun komplexe XML-Dokumente aufgebaut werden: 20294 Hunde von Riga Krimi 18,50
Abbildung 1.7: Ein gültiges Dokument
Das Wurzelelement lautet „Buch“, es enthält die vier Unterelemente „Nummer“, „Titel“, „Gruppe“ und „Preis“, die gleichberechtigt nebeneinander stehen und alle Daten beinhalten. So können mehrere Elemente hintereinander stehen und in beliebig vielen Ebenen verschachtelt werden:
22
*OWLJH ;0/'RNXPHQWH
Sandini Bib 20294 Hunde von Riga Krimi 18,50 1002 Wenn die grauen Falter flattern Krimi 13,00
Abbildung 1.8: Das Wurzelelement „Artikel“ beinhaltet nun zwei Tags „Buch“
1.3
Der Zeichensatz
Bislang habe ich Umlaute vermieden. Damit die deutschen Umlaute korrekt dargestellt werden, muss die ISO-Norm 8859-1 verwendet werden. Mit ihr können alle nord-, west- mittel- und südeuropäischen Sprachen dargestellt werden. Sie entspricht dem alten ASCII-Code. Dieser Standard wird im Prolog folgendermaßen eingegeben:
'HU =HLFKHQVDW]
23
Sandini Bib
Das Attribut „encoding“ enthält den korrekten ISO-Standard, der Umlaute und das „ß“ in der Darstellung des Browsers ermöglicht.
Abbildung 1.9: Nun werden die Umlaute korrekt dargestellt
Weitere Sprachen werden in folgender Tabelle aufgelistet: Standard
Einige Zeichen können nicht direkt eingegeben werden. Dazu gehören „&“, „“, „'“ und „"“. Eine Zeile Max & Moritz
liefert eine Fehlermeldung. Dafür stehen einige Zeichenreferenzen zur Verfügung: Zeichen
Darstellung
&
&
<
<
>
>
'
'
"
"
Tabelle 1.4: Einige Zeichen und ihre Referenzen (die vollständige Liste)
Damit kann der obige Buchtitel folgendermaßen korrekt dargestellt werden: Max & Moritz
1.5
CDATA-Abschnitte
Angenommen, Sie haben nicht nur einige wenige Zeichen, die Sie durch Zeichenreferenzen darstellen möchten. Dann kann man dem Parser explizit sagen, dass bestimmte Bereiche des XML-Dokuments ausgespart werden. Dies ist beispielsweise dann interessant, wenn Sie JavaScriptCode im XML-Dokument eingefügt haben, der allerdings wegen der Ungleichheitszeichen nicht geparst werden soll. Die Darstellung sieht folgendermaßen aus: 12 Loriot's Wum & Wendelin Unterhaltung 13 Loriot's Heile Welt Unterhaltung
$ENU]XQJHQ
25
Sandini Bib ]]>
Beim Parsen werden nun die beiden Kindelemente des Elements übergangen, was der Internet Explorer zeigt:
Abbildung 1.10: Ein Dokument mit einem CDATA-Abschnitt
1.6
Kommentare
Ebenso, wie Sie an beliebigen Stellen Absatzzeichen eingeben können, so können Sie an beliebigen Positionen des XML-Dokuments Kommentare einfügen. Sie beginnen jeweils mit einem . Also beispielsweise: Twain Marc
26
*OWLJH ;0/'RNXPHQWH
Sandini Bib Melville Hermann Steinbeck John
Abbildung 1.11: Ein Dokument mit Kommentaren
Kommentare dürfen dort im Text stehen, wo Text erlaubt ist. Am besten eignet sich die Stelle zwischen den Tags. Am Anfang eines Dokuments sind Kommentare verboten, ebenso wie innerhalb eines Tags ein Kommentar nicht erlaubt ist. Kommentare dürfen sich über mehrere Zeilen erstrecken. Da manche Browser, zum Beispiel der Internet Explorer,
.RPPHQWDUH
27
Sandini Bib
Kommentare anzeigen, sollten Sie die Kommentare vor dem Veröffentlichen des XML-Dokuments wieder löschen. Übrigens dürfen Kommentare sogar Zeichen wie „Steinbeck John -->
Abbildung 1.12: Ein Datensatz wurde ausgeblendet, d.h. auskommentiert
28
*OWLJH ;0/'RNXPHQWH
Sandini Bib
1.7
Attribute
Ebenso wie in HTML können Tags Attribute besitzen. So besitzt beispielsweise das Tabellentag
in HTML die Attribute „border“, „width“, „height“ und „bordercolor“. Das Gleiche gilt für die XML-Tags. Dort kann beispielsweise ein Telefon geschäftlich oder privat sein. Im XML-Dokument sieht das folgendermaßen aus:
Für die Namen der Attribute gelten die gleichen Regeln wie für die Namen der Tags: Sie beginnen mit einem Buchstaben, dürfen Buchstaben, Zahlen, Unterstriche und Bindestriche beinhalten. Die Werte der Attribute sind immer Text und werden immer in Anführungszeichen (einfachen oder doppelten) gesetzt. Beispiele hierzu sind sicherlich von HTML bekannt. Das Tag gibt an, dass ein Bild angezeigt wird. Das Attribut gibt den Namen (und die Quelle) des Bildes an. Es ist zwingend notwendig, da es der Hauptbestandteil des Tags ist. Weniger wichtig sind beschreibende Attribute wie WIDTH und HEIGHT. Sie können eingefügt werden (um ein Bild zu vergrößern oder zu verkleinern), müssen allerdings nicht.
1.7.1 Mehr als zwei Möglichkeiten bei Attributen Selbstverständlich sind mehr als zwei Möglichkeiten zugelassen. Die Liste kann (fast) beliebig erweitert werden:
1.7.2 xml:lang Als nützlich kann sich das Attribut xml:lang erweisen. Mit seiner Hilfe kann eine Sprache festgelegt werden. Dies ist dann interessant, wenn mehrere Sprachen (und damit auch mehrere unterschiedliche Sonderzeichen) in einem Dokument vorkommen. Beispielsweise in folgendem Dokument: Wenn der Regen niederbraust, wenn der Sturm das Feld durchsaust, bleiben Mädchen oder Buben
$WWULEXWH
29
Sandini Bib hübsch daheim in ihren Stuben. When the rain and wind are strong, when the storm roars loud and long girls and boys at home abide snug and warm by their fireside. Quand aux champs l'orage gronde, qu'il pleut partout à la ronde, les filles et les garçons demeurent à la maison. Cuando en días de tormenta la lluvia azota, violenta, los campos y la ciudad los niños de corta edad deben quedarse en su cuarto. Quando giù la pioggia scroscia ed il vento i campi affloscia le fanciulle e i bambinetti stanno in casa ben protetti. Pluviae cum defluunt, venti campos perstrepunt tum et puer et puella domi maneant in cella!
30
*OWLJH ;0/'RNXPHQWH
Sandini Bib
Abbildung 1.13: Nun werden alle Sprachen korrekt dargestellt
Im Folgenden eine kleine Auswahlliste des ISO-639-Sprachcodes: Code
Sprache
Code
Sprache
Code
Sprache
ar
Arabisch
fr
Französisch
nl
Holländisch
ch
Chinesisch
gr
Griechisch
no
Norwegisch
cs
Tschechisch
hr
Kroatisch
pl
Polnisch
da
Dänisch
hu
Ungarisch
pt
Portugiesisch
Tabelle 1.5: Der ISO-639-Sprachcode
$WWULEXWH
31
Sandini Bib Code
Sprache
Code
Sprache
Code
Sprache
de
Deutsch
is
Isländisch
ro
Rumänisch
en
Englisch
it
Italienisch
ru
Russisch
es
Spanisch
ja
Japanisch
sv
Schwedisch
fi
Finnisch
la
Lateinisch
tr
Türkisch
Tabelle 1.5: Der ISO-639-Sprachcode (Forts.)
Oft ist es für Web-Suchmaschinen praktisch, wenn die Sprache zum Inhalt angegeben ist. Deshalb können sogar Sprach-Subcodes verwendet werden: Schlagsahne Schlagrahm Schlagobers
Die folgende Liste gibt einige der Länderspezifikationen wieder: Code
Land
Code
Land
Code
Land
AU
Australien
GR
Griechenland
PL
Polen
AT
Österreich
HU
Ungarn
PT
Portugal
BE
Belgien
IS
Island
RO
Rumänien
CA
Kanada
IN
Indien
RU
Russland
CN
China
IE
Irland
SK
Slowakei
HR
Kroatien
IL
Israel
ES
Spanien
CY
Zypern
IT
Italien
SE
Schweden
CZ
Tschechien
JP
Japan
CH
Schweiz
DK
Dänemark
LI
Liechtenstein
TR
Türkei
FI
Finnland
LU
Luxemburg
GB
Großbritannien
FR
Frankreich
NL
Niederlande
US
USA
DE
Deutschland
NO
Norwegen
YU
Jugoslawien
Tabelle 1.6: Der ISO-3166-Ländercode
Ein schönes Beispiel für internationale Zeichendarstellung findet sich im Unterordner des XML Spy, welcher im nächsten Kapitel näher beschrieben wird.
32
*OWLJH ;0/'RNXPHQWH
Sandini Bib
Abbildung 1.14: So etwas sollte durchaus möglich sein ...
1.7.3 Ein oder mehrere Attribute im Dokument Die Elemente können mit mehreren Attributen beispielsweise so aussehen: Südamerikanische Küche Berechnungen in Excel Gelbe Seiten
Abbildung 1.15: Daten mit Attributen
$WWULEXWH
33
Sandini Bib
Natürlich kann das Attribut weggelassen werden. Die türkische Küche Visio 2000 professionell Rosa Seiten
Abbildung 1.16: Die Attribute sind optional
Achten Sie dabei auf die korrekten Anführungszeichen bei den Attributen. 4712
Übrigens könnte das Attribut auch in einfachen Anführungszeichen geschrieben werden: 4713
Da XML nur Text verwaltet, müssen Attribute – anders als in HTML – in Anführungszeichen geschrieben werden. Was in HTML so lautet:
muss in XML wie folgt geschrieben werden:
1.7.4 Attribut oder Element? Vielleicht fragen Sie sich, warum überhaupt mit Attributen gearbeitet wird. Man könnte ein Buchelement, das folgenden Tag besitzt:
34
*OWLJH ;0/'RNXPHQWH
Sandini Bib
auch folgendermaßen schreiben: Visio 2000 Rene Martin Addison Wesley 3-8273-1626-X
Abbildung 1.17: Attribut oder Element – wo liegt der Unterschied?
Über diese Frage ist schon viel geschrieben und diskutiert worden. Es gibt allerdings einige – zugegeben schwache – Argumente für oder gegen die eine oder die andere Schreibweise. Für Attribute sprechen folgende Argumente: • Die Anzahl der Zeichen ist geringer, was sich bei sehr großen Datenmengen bemerkbar macht.
$WWULEXWH
35
Sandini Bib
• In einer DTD kann der Wertebereich vorgegeben, das heißt eingeschränkt werden. (Was eine DTD ist, wird im nächsten Kapitel erläutert.) • In einer DTD kann ein Vorgabewert definiert werden. Nachteile der Attribute: • Man kann Attribute nicht schachteln. • Attribute haben keine festgelegte Reihenfolge. • Viele Attribute sind vom Menschen schwer zu lesen. Vorteile der Kind-Elemente: • Eine Schachtelung ist möglich. • Eine Reihenfolge kann in der DTD festgelegt werden. • Durch Entitäten können sich wiederholende Werte abgekürzt werden. • Viele Kind-Elemente können vom Menschen noch immer gut gelesen werden. Die Nachteile von Kind-Elementen: • Leicht erhöhter Platzverbrauch • Kind-Elemente sind etwas schwieriger zu programmieren.
1.8
Zusammenfassung
Damit sind die wichtigsten Punkte beschrieben, die ein gültiges XMLDokument ausmachen. Jedes XML-Dokument beginnt mit einem so genannten Prolog. Darin befindet sich die XML-Deklaration:
Für uns deutsch schreibende Menschen ist die Deklaration des Ländercodes wichtig:
Das Dokument ist aus Elementen aufgebaut. Der Inhalt der Elemente wird durch Tags umschlossen. Der Anfangstag muss gleich lauten wie der Endtag – auch die Groß- und Kleinschreibung muss bei beiden die gleiche sein. Ein Tag beginnt mit einer spitzen, öffnenden Klammer und endet mit einer spitzen, schließenden Klammer. Das Endtag unterscheidet sich vom Anfangstag durch einen Schrägstrich. Jedes XML-Dokument besitzt ein Wurzelelement. Alle anderen Elemente sind – streng hierarchisch – darauf aufgebaut. Ein Element umschließt immer vollständig ein anderes Element – Verschränkungen gibt es in XML nicht.
36
*OWLJH ;0/'RNXPHQWH
Sandini Bib
Ein leeres Element wird als einfacher Tag durch einen Schrägstrich am Ende gekennzeichnet. Das Dokument kann Kommentare beinhalten. Elemente können Kommentare einschließen. Für die Namen der Tags und Attribute gelten die üblichen – strengen – Regeln: nur Buchstaben und Zahlen.
1.9
Fragen zu Kapitel 1
Allgemeine Fragen Welche der folgenden Aussagen ist falsch? 1. Im Prolog sollte die XML-Deklaration
stehen. 2. Im Prolog stehen optional Verarbeitungsanweisungen und Kom-
mentare. 3. Ein XML-Dokument kann kein, ein oder mehrere Stammelemente
enthalten. 4. Jedes wohlgeformte XML-Dokument muss genau ein Stammelement
enthalten. 5. Alle anderen Elemente befinden sich im Stammelement. 6. Teile eines XML-Dokuments heißen Entitäten und können außer-
halb des Dokuments liegen. 7. Leerzeichen sind verboten.
Fehler In den folgenden vier Dokumenten hat sich jeweils ein Fehler eingeschlichen. Welcher? Petzi Lars Pu
38
*OWLJH ;0/'RNXPHQWH
Sandini Bib
Struktur fortsetzen Wie muss das Dokument korrekt beendet werden?
)UDJHQ ]X .DSLWHO
39
Sandini Bib
Sandini Bib
2
Wohlgeformte XMLDokumente
le
n e rn
Nachdem Sie nun wissen, wie XML-Dokumente aussehen, möchte ich einen Schritt weiter gehen. Ein Dokument kann die Elemente, die es beinhaltet, als Informationsblock darstellen. Eine solche beschreibende Struktur wird DTD oder Schema genannt und ist nicht nötig. Wie bei allem im Leben gibt es Argumente für und gegen eine DTD oder ein Schema.
2.1
Die DTD
Die Struktur eines XML-Dokuments wird baumartig gesehen. Jedes Dokument darf nur genau einen „Baum“ haben. Sein tiefstes Element wird Wurzel, Wurzelknoten (Root Element) oder Stammelement genannt. Es enthält Kindelemente. Ähnlich wie in HTML existieren auch in XML DOCTYPE-Tags. Dort wird festgelegt, welche Elemente und welche Attribute vorkommen dürfen. Oder genauer: Es werden alle Informationen des Dokuments beschrieben. Die Struktur wird darin festgelegt. In XML sieht eine DTD beispielsweise folgendermaßen aus: Spitz, das war ihr erstes Wort.
oder allgemein:
'LH '7'
41
Sandini Bib
Der Internet Explorer zeigt lediglich an, dass eine DTD vorhanden ist, aber nicht, wie sie aussieht.
Abbildung 2.1: Die Anzeige mit DOCTYPE
Für den Namen der Definition und den Namen des Tags gelten die gleichen Regeln. Der Name darf aus Buchstaben, Ziffern und Unterstrichen bestehen, allerdings nicht aus Umlauten, nicht aus dem „ß“, nicht aus Satz- oder Sonderzeichen. Das erste Zeichen darf keine Ziffer sein. Ein gültiger Name einer DTD ist folglich Busch Wilhelm_Busch WBUSCH1877
Falsch dagegen W.Busch *Busch* Wilhelm Busch
Diese Definition des Tags (natürlich werden noch mehr Tags hinzukommen) werden als DTD (Document Type Definition) bezeichnet. Jedes Element, welches in einer XML-Datei vorkommt, muss in der DTD deklariert werden. Sie definiert die Struktur der Tags. Angenommen, es werden zwei umschließende Tags verwendet, dann sieht die DTD wie folgt aus: Max und Moritz
Abbildung 2.3: Das Dokument wird komplexer
Das „Gegenteil“ von Unterelementen ist #PCDATA (Parsed Character Data). Dies ist die Abkürzung für „geparste Zeichendaten“, auf Deutsch: Inhalt. Auch Zahlen sind in XML Text. Dürfen Elemente mehrmals vorkommen, dann werden sie mit einem Sternchen gekennzeichnet: ]> Max und Moritz Die fromme Helene Fips der Affe
44
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Abbildung 2.4: Mehrere Tags
Das bedeutet, dass der Element-Tag in der DTD folgenden Aufbau hat:
Über den Namen braucht nichts gesagt zu werden – die Regeln wurden bereits erläutert. Interessant ist der Inhalt. Folgende Möglichkeiten stehen Ihnen für „Inhalt“ zur Verfügung: • EMPTY und ANY. Das erste Inhaltsattribut bedeutet, dass es sich um ein leeres Element handelt, ANY steht für jede Art von Inhalt. • Untergeordnete Elemente • Daten (#PCDATA) • Gemischte Inhalte
2.2.1 Anzahl der Elemente In der Regel haben Sie untergeordnete Listen von Elementen. Taucht ein Element genau ein Mal auf, dann wird es ohne Zeichen erwähnt. Genau ein Verfasser wird so dargestellt:
Hat ein Buch einen Verfasser, einen Autor und einen Herausgeber, dann können sie mit Kommata getrennt aufgelistet werden:
'LH 5HJHOQ GHU '7'
45
Sandini Bib
Was geschieht nun allerdings mit Büchern, die mehrere Verfasser haben? Oder zu mehreren Gruppen gehören? Oder umgekehrt: Manche Bücher haben keinen Untertitel, andere schon. Manche keinen Übersetzer, andere sind aus dem Englischen oder Französischen übersetzt. Für diesen Zweck können die Tags in der DTD spezifiziert werden. Darf der Verfasser mehrmals auftauchen, dann wird dies durch ein Sternchen dargestellt: ]>
Das bedeutet, dass das Element „Verfasser“ gar nicht, einmal oder beliebig oft auftauchen kann. Soll mindestens ein Verfasser auftauchen, dann ist das Pluszeichen zu verwenden:
Hat der Autor immer einen Vor- und einen Zunamen, dann werden sie hintereinander geschrieben:
Ist der Vorname optional, der Zuname allerdings zwingend nötig, dann wird dies durch ein Fragezeichen gekennzeichnet:
Dies bedeutet, dass er fehlen darf. Alle übrigen Eigenschaften bleiben wie gehabt.
2.2.2 Und- und Oder-Verknüpfungen Im folgenden Beispiel sind die beiden Elemente Zuname und Vorname zwingend nötig. Das Komma weist auf eine Und-Verknüpfung hin.
Hat das Element einen Autor oder einen Herausgeber (also eine OderVerknüpfung), dann wird es durch einen senkrechten Strich (Pipe) dargestellt:
Hat es einen Autor oder einen Herausgeber oder keinen von beiden oder beide, dann kann man es wie folgt schreiben:
46
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
2.2.3 Kombination aus Anzahl und Verknüpfung Und so können mehrere Elemente beliebig geschachtelt werden. Ein Autor hat genau einen Zunamen:
Ein Autor hat sowohl einen Zunamen als auch Vornamen:
Ein Autor hat einen Zunamen oder einen Vornamen:
Ein Autor hat einen oder keinen Zunamen, einen oder keinen Vornamen:
Ein Autor hat keinen, einen oder mehrere Zunamen, keinen, einen oder mehrere Vornamen:
Ein Autor hat keinen oder einen Zunamen, keinen, einen oder mehrere Vornamen:
Ein Autor hat beliebig viele Zunamen und Vornamen (nacheinander):
Und nun sind alle Varianten aus Zunamen und Vornamen erlaubt:
Dieses Spiel kann man noch weiter spielen. Der müßige Leser möge, falls er Langeweile hat, sich weitere Kombinationen ausdenken.
2.2.4 Ein größeres Beispiel einer DTD
'LH 5HJHOQ GHU '7'
47
Sandini Bib Kurzbeschreibung (#PCDATA)> Form (Preis, Seitenzahl, ISBN)> Preis (#PCDATA)> Seitenzahl (#PCDATA)> ISBN (#PCDATA)>
]>
Für Elemente stehen folgende Varianten zur Verfügung: Zeitschrift (Titel, Erscheinungsjahr, Nummer?)> CD ((Titel|Gruppe), Aufnahmejahr> NonBook1 (Ware)+> NonBook2 (Ware)*>
Um zu klären, wo Text eingegeben wird, muss bei der Deklaration #PCDATA geschrieben werden:
Damit kann man „verrückte“ Dinge anstellen. Einige DTDs müssen schon zwei Mal betrachtet werden, bis ihre Definition „entschlüsselt“ ist. Versuchen Sie es doch einmal mit folgenden abstrakten Elementen: Inhaltsmodell
Elementfolge
(A)
A
(A, B?)
A A B
(A, B?, (C | D))
A A A A
C D B C B D
(A, B?, (C | D+), E)
A A A A A A A
C D D D B B B
E E D D C D D
E D E E E D E
(und weitere Möglichkeiten) (A, (C?, D)?)
A A C D A D
(A, (B? | C)?)
A A B A C
Tabelle 2.1: Einige Beispiele für das Inhaltsmodell
48
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Dass Daten in der folgenden Form dargestellt werden, wurde oben schon mehrfach beschrieben:
Wie sieht nun eine Darstellung für gemischte Daten aus? Die Syntax hierfür lautet:
Wichtig ist, dass zuerst die Daten (#PCDATA) in der Liste stehen und dann die einzelnen Elemente. Die gesamte Gruppe kann und muss wiederholbar sein, nicht aber jedes einzelne Element. Es können weder zwei Datenelemente in einem Element hintereinander stehen (was trivial ist), noch darf ein Element wiederholt werden. Folgende DTD ist also erlaubt:
2.3
Wohlgeformt und gültig
Zwei weitere Begriffe sind in diesem Zusammenhang wichtig. Ein XMLDokument wird „wohlgeformt“ („well-formed“) genannt, wenn es den Regeln genügt, wie sie in Kapitel 1 beschrieben wurden. Also beispielsweise: Der Struwwelpeter
Abbildung 2.5: Ein wohlgeformtes Dokument
:RKOJHIRUPW XQG JOWLJ
49
Sandini Bib
Ein Dokument wird „gültig“ („valid“) genannt, wenn es eine beschreibende DTD besitzt, wie beispielsweise: ]> Max und Moritz
Abbildung 2.6: Ein gültiges Dokument
Wer legt das fest? Das World Wide Web-Consortium (W3C) hat Wohlgeformtheit als wichtigstes Kriterium eines XML-Dokuments festgelegt. Damit reagiert es auf die Entwicklung von HTML. Zwar gibt es auch hier Empfehlungen, aber die Praxis hat gezeigt, dass manche Browser eigene Elemente definieren, welche andere nicht kennen. Oder bestimmte Elemente werden unterschiedlich dargestellt. Um der Tendenz entgegenzuwirken, dass in naher Zukunft mehrere XML-Spezifikationen vorliegen, wurden diese Empfehlungen herausgebracht. Das W3C ist eine Mitgliederorganisation, die aus zirka 400 Mitgliedern weltweit besteht. Es wird vom Massachusetts Institute of Technology, Laboratory for Computer Science (MIT/LCS) in den USA, dem Institut National de Recherche en Informatique et en Automatique (INRIA) in Europa und der Keio University Shonan Fujisawa Campus in Japan verwaltet. Es beschäftigt ungefähr 50 hauptberufliche Mitarbeiter. Das W3C veröffentlicht keine Regeln oder Gesetze, sondern lediglich Emp-
50
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
fehlungen. Diese sind nachzulesen unter www.w3c.org/xml. Fassen wir zusammen: • Ein wohlgeformtes XML-Dokument besitzt einen Prolog mit einer Deklaration () und optional Kommentare und Verarbeitungsanweisungen. Der Hauptteil enthält genau ein Wurzelelement, welches in der Regel andere Elemente beinhaltet. Die Namen der Elemente folgen bestimmten Regeln – dies wurde am Anfang des ersten Kapitels beschrieben. Die Elemente sind korrekt ineinander verschachtelt. • Ein XML-Dokument kann auf Elemente aus anderen Dokumenten verweisen. Dann muss für jede dieser Entitäten die gleiche Gültigkeitsregel gelten wie für das Originaldokument. Oder im Fachjargon ausgedrückt: Jede geparste Entität muss wohlgeformt sein. • Nicht jedes wohlgeformte XML-Dokument muss gültig sein. So setzen die meisten Parser, unter anderem auch der Internet-Explorer von Microsoft, Wohlgeformtheit voraus, nicht aber unbedingt Gültigkeit. Ist das Dokument dennoch gültig, dann wird die DTD zur Validierung herangezogen. Achtung: der Internet Explorer überprüft lediglich auf Wohlgeformtheit, nicht auf Gültigkeit!
Abbildung 2.7: Obwohl die DTD nicht korrekt für das Dokument definiert wurde (oder das Dokument nicht den Konventionen der DTD folgt), ...
:RKOJHIRUPW XQG JOWLJ
51
Sandini Bib
Abbildung 2.8: ... zeigt der Internet Explorer die Datei korrekt an.
2.4
Weitere Tags
Und mit diesem Wissen können nun komplexe XML-Dokumente aufgebaut werden: ]> 20294 Hunde von Riga Krimi 18,50
52
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Abbildung 2.9: Ein gültiges Dokument
Das Wurzelelement lautet „Buch“, es enthält die vier Unterelemente „Nummer“, „Titel“, „Gruppe“ und „Preis“, die gleichberechtigt nebeneinander stehen und alle Daten beinhalten. Dies wurde in der DTD definiert. Dort wird erklärt, welche Tags das Wurzeltag beinhaltet. Sie stehen durch Kommata voneinander getrennt:
Danach wird jedes einzelne Tag aufgelistet und „erläutert“. So können mehrere Elemente hintereinander stehen und in beliebig viele Ebenen verschachtelt werden: ]> 20294 Hunde von Riga Krimi 18,50
:HLWHUH 7DJV
53
Sandini Bib 1002 Wenn die grauen Falter flattern Krimi 13,00
Abbildung 2.10: Das Wurzelelement „Artikel“ beinhaltet nun zwei Tags „Buch“
Zuerst beinhaltete das Wurzelelement genau einen Tag. Dies wird so dargestellt:
Enthält das Wurzelelement „Artikel“ einen oder keinen Tag „Buch“, dann muss man ein Fragezeichen verwenden:
Soll explizit darauf verwiesen werden, dass das Element mehrmals vorkommt, dann wird dies durch ein Pluszeichen gekennzeichnet:
Das Pluszeichen steht also für mindestens ein Element. Will man ausdrücken, dass entweder kein Element, ein Element oder beliebig viele
54
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Elemente auftauchen dürfen, dann wird dies mit einem Sternchen gekennzeichnet:
2.5
Kommentare
Kommentare wurden bereits in Kapitel 1 erläutert. Sie werden natürlich nicht in einer DTD deklariert. Kommentare dürfen zwar nicht am Anfang eines Dokuments oder innerhalb einer DTD oder Tags stehen, sind aber vor und nach einer DTD erlaubt. Das folgende Beispiel ist Ihnen aus Kapitel 1 bekannt – es wurde um eine DTD erweitert: ]> Twain Marc Melville Hermann Steinbeck John
.RPPHQWDUH
55
Sandini Bib
Abbildung 2.11: Ein Dokument mit Kommentaren
2.6
Attribute
Attribute wurden bereits im vorhergehenden Kapitel erläutert und beschrieben. Auch sie werden in einem gültigen Dokument deklariert: ]>
Zuerst wird das Element definiert („Telefon“). Dann wird festgelegt, dass dieses Element ein Attribut besitzt („art“). Danach stehen die möglichen Attributwerte durch Pipes getrennt hintereinander. #REQUIRED bedeutet, dass ein Attribut unbedingt nötig ist. Wäre dies nicht der Fall, so würde der Zusatz #IMPLIED gesetzt.
56
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Im Dokument wird dann folgendermaßen erläutert:
Für die Namen der Attribute gelten die gleichen Regeln wie für die Namen der Tags: Sie beginnen mit einem Buchstaben, dürfen Buchstaben, Zahlen, Unterstriche und Bindestriche beinhalten. Die Werte der Attribute sind immer Text und werden immer in Anführungszeichen (einfachen oder doppelten) gesetzt.
2.6.1 Mehr als zwei Möglichkeiten bei Attributen Selbstverständlich sind mehr als zwei Möglichkeiten zugelassen. Die Liste kann (fast) beliebig erweitert werden:
Damit können auch mehrere Schreibweisen ermöglicht werden:
Die allgemeine Syntax von Attributen lautet:
Typ kann folgende Werte annehmen: Typ
Beschreibung
CDATA
einfache Daten, einfacher Text
Aufzählung
eine Liste von Vorgabewerten, die verwendet werden müssen
ENTITYS
mehrere Namen von Entitäten, die in der DTD deklariert sein müssen. Sie werden durch ein Leerzeichen voneinander getrennt
ENTITY
eine Entität, die in der DTD deklariert sein muss
ID
XML-Name, der eindeutig sein muss
IDREF
enthält den Wert des ID-Attributs eines Elements, auf das sich das aktuelle Element bezieht
IDREFS
zeigt mehrere IDs an, die durch Leerzeichen voneinander getrennt sind
NMTOKEN
zeigt einen korrekten XML-Namen an
Tabelle 2.2: Die verschiedenen Attribut-Typen
$WWULEXWH
57
Sandini Bib Typ
Beschreibung
NMTOKENS
zeigt mehrere richtige XML-Namen an, die durch Leerzeichen voneinander getrennt sind
NOTATION
zeigt einen Notationsnamen an, der in der DTD deklariert werden muss
Tabelle 2.2: Die verschiedenen Attribut-Typen (Forts.) Standardwert
Beschreibung
Wert
Ein einfacher Textinhalt. Er wird in Anführungszeichen geschrieben.
#IMPLIED
Gibt an, dass kein Standardwert für dieses Attribut vorhanden ist und das Attribut nicht verwendet werden muss
#REQUIRED
Gibt an, dass dem Attribut ein Wert zugewiesen werden muss
#FIXED Wert
„Wert“ ist der Inhalt des Attributs, welches das Attribut haben muss.
Tabelle 2.3: Die verschiedenen Standardwerte des Attributs
2.6.2 Ein Default-Wert Soll ein Wert als Standard angenommen werden, dann kann er ans Ende der Liste gestellt werden:
2.6.3 Ein oder mehrere Attribute im Dokument Wird in der DTD festgelegt, dass das Element „Buch“ eine „Kategorie“ besitzen muss, also:
dann könnten die Elemente beispielsweise so aussehen: Südamerikanische Küche Berechnungen in Excel Gelbe Seiten
58
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Abbildung 2.12: Daten mit Attributen
Nur wenn das Element mit #IMPLIED angegeben wird, dann kann das Attribut weggelassen werden. Die türkische Küche Visio 2000 professionell Rosa Seiten
Abbildung 2.13: Die Attribute sind optional
$WWULEXWH
59
Sandini Bib
Als Alternative zu #IMPLIED und #REQUIRED steht #FIXED zur Verfügung – damit verfügen alle Elemente über das gleiche Attribut VBA VBA VBA
Buch Kategorie CDATA #FIXED "EDV"> mit Word lernen mit Excel lernen mit Access lernen
Abbildung 2.14: Ein festes Attribut für alle Tags
Wenn über Aufzählungen Werte vorgegeben werden, dann muss einer der Werte, der in der Aufzählung vorkommt, verwendet werden: ]> 4711 4712 4713
Dies sind die wichtigsten Deklarationsmöglichkeiten der Attribute. Der Vollständigkeit halber sollen noch die übrigen Varianten erläutert werden. Wird das Attribut nicht vom Typ CDATA, sondern als NMTOKEN gesetzt, dann sind nur Attributwerte zulässig, die den Regeln der XML-Namen folgen, das heißt: sie bestehen nur aus Buchstaben und Zahlen, das erste Zeichen ist keine Ziffer, sie besitzen keine Sonderzeichen und Leerzeichen.
60
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Eine Kategorie wie im folgenden Beispiel wäre folglich falsch, weil sie Leerzeichen enthält. Südamerikanische Küche
Richtig dagegen sind die anderen beiden Beispiele: Berechnungen in Excel Gelbe Seiten
Wird statt NMTOKEN der Attributtyp NMTOKENS verwendet, dann können mehrere NMTOKEN durch Leerzeichen getrennt hintereinander geschrieben werden: Das ALDIdente-Kochbuch
Diese Varianten sind für C++, JavaScript und andere Programmiersprachen interessant, weil dadurch die Namen gewissen Regeln unterliegen und leicht weiterverarbeitet werden können. Wird der Attribut-Typ ID verwendet, dann muss sich jedes Attribut von den anderen unterscheiden. Dies ist aus Datenbanksystemen als Zähler oder Autowert bekannt: Südamerikanische Küche Berechnungen in Excel Baudolino
Mit dem Attribut-Typ IDREF wird versucht auf eine schon existierende ID zuzugreifen. Vielleicht verdeutlicht das folgende Beispiel diesen Sachverhalt: VBA mit Office 2000 lernen XML insider
Werden Attribut-Typen ENTITY oder ENTITIES verwendet, dann kann auf ein bestimmtes Element, auf eine Entität eben, zurückgegriffen werden: VBA mit Office 2002 lernen VBA mit Word 2002 lernen
$WWULEXWH
61
Sandini Bib
Mehr zum Thema XML-Daten und Dateien, die außerhalb von XML liegen (beispielsweise Bilder), finden Sie im Kapitel 8 „XML, HTML und XSL“.
2.6.4 Mehrere Attribute Problemlos können auch mehrere Attribute vergeben werden. Dazu werden in der DTD die einzelnen Attributnamen hintereinander geschrieben:
Werden keine Werte vorgegeben, so muss „CDATA“ (character data) hinter dem Attributnamen folgen. Muss das Attribut gesetzt werden, so wird #REQUIRED verwendet, kann es gesetzt werden, so steht #IMPLIED. Und diese Elemente können anschließend mit Attributen versehen werden: ]> Twain Marc 4711 Melville Hermann 4711 Steinbeck
62
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib John 4712
Abbildung 2.15: Mehrere Attribute
Achten Sie dabei auf die korrekten Anführungszeichen bei den Attributen. Nicht in der DTD
sondern bei den Elementen: 4712
Übrigens könnte das Attribut auch in einfachen Anführungszeichen geschrieben werden: 4713
Da XML nur Text verwaltet, müssen Attribute – anders als in HTML – in Anführungszeichen geschrieben werden. Was in HTML so lautet:
$WWULEXWH
63
Sandini Bib
muss in XML wie folgt geschrieben werden:
2.7
Die DTD auslagern
Inzwischen ist die DTD schon sehr angewachsen. Das Dokument besteht nun aus seiner Definition und seinem Inhalt. Irgendwann stellt sich die Frage, ob man die DTD nicht auslagern soll. Eine externe DTD könnte folgende Gestalt haben:
Sie wird unter dem Datenamen „artikelliste“ mit der Endung „dtd“ gespeichert.
Abbildung 2.16: Die ausgelagerte DTD
Um sie in eine XML-Datei einzubinden, wird die -Deklaration um den Befehl standalone="no" erweitert:
Das Attribut standalone = "yes" legt fest, dass alle verwendeten EntityReferenzen (also beispielsweise die DTD) im Dokument zu finden sind. Oder anders ausgedrückt: Das Dokument ist in sich geschlossen. Wird der Wert auf no gesetzt, so wird eine externe DTD verlangt. Einige Parser ignorieren übrigens dieses Attribut.
64
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Und dann erfolgt der Verweis, wo sich die DTD befindet:
Der Verweis kann relativ sein, wie in obigem Beispiel. Das hat den Vorteil, dass ein Verschieben oder Umbenennen des Ordners nicht gekennzeichnet werden muss. Will man dagegen einen absoluten Pfad verwenden, so könnte dieser beispielsweise wie folgt geschrieben werden:
Oder man gibt als Pfadangabe eine Web-Adresse an:
Abbildung 2.17: Das Dokument mit ausgelagerter DTD
Ob Sie die DTD auslagern oder als interne DTD deklarieren, ist Ihnen überlassen. Die Vorteile der externen DTD liegen auf der Hand: • Die externe DTD kann geändert werden, ohne dass das XML-Dokument verändert wird. • Man kann mehrere DTDs zur Verfügung stellen und „schnell“ von einer DTD auf eine andere umschalten. Prinzipiell spielt es für den Aufbau der DTD keine Rolle, wo sie sich befindet. Sie enthält lediglich die Informationen, welche Elemente welchen Inhalt haben, welche Attribute vorkommen und welche Werte sie annehmen dürfen. Wenn Sie sich zwingen möchten eine DTD zu verwenden, dann können Sie dies in der XML-Deklaration festlegen:
'LH '7' DXVODJHUQ
65
Sandini Bib
Das Attribut standalone = "yes" legt fest, dass alle verwendeten EntityReferenzen im Dokument zu finden sind. Wird der Wert auf no gesetzt, so wird eine externe DTD verlangt.
2.8
Abkürzungen
In Kapitel 1 wurde bereits erläutert, dass einige Zeichen nicht direkt eingegeben werden können. Dazu gehören „&“, „“, „'“ und „"“. Eine Zeile Max & Moritz
liefert eine Fehlermeldung. Der obige Buchtitel kann folgendermaßen korrekt dargestellt werden: Max & Moritz
Diese Abkürzungen, die allgemein vordefiniert sind, werden „Entitätsreferenzen“ genannt. Jede Entitätsreferenz wird beim Parsen durch das entsprechende Zeichen ersetzt. Mit dieser Methode können in der DTD auch eigene Abkürzungen definiert werden: ]> 12 Loriot's Wum & Wendelin &U; 13 Loriot's Heile Welt &U;
66
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Abbildung 2.18: Das Ergebnis der Entitäten wird korrekt dargestellt
In der DTD wird eine Abkürzung (ENTITY) festgelegt, wobei zuerst der Name (hier: „U“) und anschließend die Definition folgt (hier: „Unterhaltung“). Wichtig ist, dass der abgekürzte Text in Anführungszeichen steht, da er auch Leerzeichen beinhalten darf. Wird die Referenz verwendet, so wird sie mit einem Ampersand (Et-Zeichen) begonnen und mit einem Semikolon beendet. Abkürzungen können auch auf die DTD angewendet werden. Angenommen, in einer DTD tauchen mehrmals die gleichen Attribute auf, die für verschiedene Elemente verwendet werden so können die Attribute ausgelagert werden. Wie Attribute in der DTD dargestellt werden, wird weiter unten erläutert. ]>
$ENU]XQJHQ
67
Sandini Bib
Dies wäre natürlich viel Schreibarbeit. Deshalb kann man sich diese Attribute auslagern:
Beachten Sie, dass bei der Deklaration der Entitätsreferenz ein Prozentzeichen auftauchen muss. In den Attributen wird diese Entität ebenfalls mit einem Prozentzeichen verwendet:
Das abschließende Semikolon ist von den anderen Entitäten und von HTML (ä) bekannt. Dies funktioniert nicht in einer internen DTD – die DTD muss ausgelagert sein (hier wird sie unter „artikelliste2.dtd“ gespeichert. Wie das Einbetten funktioniert, wird in Kapitel 5.10 beschrieben. Das Ergebnis der Datei, die diese DTD verwendet, sieht wie folgt aus: 31 Herr der Ringe Phantasy 44,90 32 Der Hobbit Phantasy 19,90
68
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Abbildung 2.19: Ein XML-Dokument nimmt Bezug auf eine externe DTD, in der Entitätsreferenzen verwendet werden
Entitäten haben noch eine weitere, sehr leistungsstarke Seite: Man kann sie auslagern.
2.9
Teile des Dokuments auslagern
Mit Hilfe der DTD können Teile eines XML-Dokuments ausgelagert werden. Das hat den Vorteil, dass bestimmte Teile von mehreren Dokumenten verwendet werden können, ohne dass sie jedes Mal in dem Dokument wiederholt werden müssen. Ein Dokument („bsp17.xml“) hat folgende Gestalt: 111 Original italienische Pasta &K; 112 Biologisch kochen und backen &K;
7HLOH GHV 'RNXPHQWV DXVODJHUQ
69
Sandini Bib
Beachten Sie, dass in diesem Dokument keine Versionsnummer und keine Doctype-Definition steht. Vor allem eine DTD würde in jedem Parser einen Fehler hervorrufen. Diese Daten werden nun durch eine andere Datei, welche eine interne DTD besitzt, aufgerufen: ]> &Daten;
Innerhalb der DTD wird als Entität „Daten“ deklariert. Mit dem Attribut SYSTEM "bsp17.xml" wird auf eine Datei verwiesen – in diesem Falle befindet sie sich im gleichen Ordner wie die Originaldatei. Man kann auch – wie hier geschehen – in der DTD weitere Entitäten deklarieren („K“ und „U“). Nun wird die Datei (&Daten;) aufgerufen und eingebunden.
Abbildung 2.20: In eine XML-Datei wird eine andere Datei eingebunden
70
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
2.10 Namensräume Inzwischen dürfte klar sein, dass Sie keine doppelt vorkommenden Namen verwenden dürfen. Ein Element darf also nicht zwei Attribute besitzen, die den gleichen Namen tragen:
So etwas führt in einem Parser unweigerlich zu einem Fehler. Was geschieht nun aber, wenn auf verschiedene Dokumente verwiesen wird, in denen sich Elemente mit dem gleichen Namen befinden. Wie kann zwischen ihnen unterschieden werden? Die Antwort auf diese Frage lautet: Man muss einen Namensraum definieren. Der Namensraum wird durch einen Doppelpunkt vom Tagnamen oder Attributnamen getrennt. Um einen neuen Namensraum zu definieren, wird in das Wurzelelement als Attribut ein Verweis auf eine andere Datei eingefügt. Das Attribut beginnt dabei mit xmlns, gefolgt von einem Doppelpunkt und anschließend vom neuen Namensraumnamen. „ns“ steht in „xmlns“ für „namespace“, also Namensraum. 12158 Rubinroter Dschungel Unterhaltung 8,80
Abbildung 2.21: Die Erweiterung mit einem Namensraum
1DPHQVUlXPH
71
Sandini Bib
Das „ns“ am Ende von xmlns steht für „Namespace“. Diesem Attribut wird ein eindeutiger Bezeichner zugeordnet, der in der Regel ein Uniform Resource Identifier (URI) ist. Der Grundgedanke der Verwendung von Namensräumen ist der Verweis auf mehrere Dokumente. Also beispielsweise auf zwei: 12195 Jacke wie Hose Unterhaltung 14,90 20 VBA-Magazin EDV 19,80
Abbildung 2.22: Zwei Namensräume werden verwendet
72
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Im Wurzelelement werden zwei Namensräume definiert („Buch“ und „Zeitschrift“), mit denen nun die einzelnen Tags genauer definiert werden. Damit können auch Attribute beschrieben werden: 19,80
URI ist keine „Adresse“ im Sinne von Speicherort von Daten, sondern hat primär die Aufgabe, Eindeutigkeit für jedes Tag zu gewährleisten. Ein Namensraum muss übrigens nicht unbedingt global deklariert sein – er kann auch nur lokal verwendet werden. Im nächsten Beispiel befindet sich der Namensraum „Zeitschrift“ lediglich in einem Element: 00571 Venusneid Unterhaltung 36,00 20 VBA-Magazin EDV 19,80
Die Verwendung von Namensräumen taucht im Kapitel 8 erneut auf, wenn es darum geht, HTML-Tags in ein XML-Dokument einzubetten. Dies funktioniert natürlich nur durch einen korrekten Hinweis mit dem Attribut xmlns:html="http://www.w3.org/TR/REC-html40"
Dann können die HTML-Befehle verwendet werden: Buch Nummer 1
Doch dazu erst später mehr.
1DPHQVUlXPH
73
Sandini Bib
2.11 Schemata Die oben beschriebenen DTDs haben einige gewaltige Nachteile. Viele Dinge, die aus Datenbanktechniken bekannt sind, können mit Hilfe einer DTD nicht gelöst werden. Man kann weder Datentypen festlegen noch Typen und Werte beschränken, die Anzahl von Elementen begrenzen oder Ähnliches. Deshalb hat das W3C eine Lösung hervorgebracht, die versucht, Abhilfe zu schaffen: das XML-Schema. Gerade Microsoft war ein Anhänger von XML-Schema, welches im Internet Explorer implementiert wurde. Da allerdings der Internet Explorer keine Überprüfung des Schemas vornimmt und sich die XML-Spezifikation geändert hat, musste Microsoft seine Implementierung anpassen.
2.11.1 Verweis auf eine Schema-Datei Anders als bei einer DTD liegt ein Schema immer extern als eine Datei vor. Lassen Sie uns das an einem kleinen Beispiel ansehen, welches im Laufe des Kapitels komplexer wird. Gegeben sei ein einfaches XMLDokument: 113 E.T.A. Hoffmann Biografie 8.80
Damit diese Datei mit einer Schema-Datei verbunden werden kann, wird ihr im Prolog angegeben, wo sich diese Datei befindet. Der Pfad kann dabei wieder lokal (so wie hier) oder global angegeben werden: 113 E.T.A. Hoffmann Biografie 8.80
Wichtig ist, dass der Hinweis im Wurzelelement zu finden ist. Die Schemadatei hat üblicherweise die Endung „xsd“. Der verwendete Namensraum lautet „http://www.w3.org/2001/XMLSchema“. Hätte die XMLDatei einen Namensraum verwendet, dann hätten wir das Schema mit dem Attribut „schemaLocation“ darstellen müssen:
74
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Die Schemadatei selbst ist ein gültiges XML-Dokument. In unserem Beispiel könnte es folgende Gestalt haben:
Das Wurzelelement lautet . Der zugehörige Namensraum heißt „http://www.w3.org/2001/XMLSchema“. Normalerweise besitzt das Schema ein Namenspräfix. Üblich ist „xs“; einige Programmierer verwenden allerdings auch „xsd“. Das -Wurzelelement besitzt nun Kindelemente mit dem Präfix „xs“. Ihr Attribut „name“ legt den Namen der Kindelemente fest, „type“ den Datentyp. Beachten Sie, dass „decimal“ die US-amerikanische Schreibweise als 8.80 verlangt.
2.11.2 Einfache Datentypen Folgende Typen stehen zur Verfügung: Typ
Beschreibung
string
alle Zeichenketten
boolean
„true“ oder „false“
byte
Zahlen zwischen 0 und 255
integer
eine ganze Zahl
long
eine ganze Zahl zwischen -9.223.372.036.854.775.808 und 9.223.372.036.854.775.807
decimal
eine beliebige Zahl
float
eine beliebige Fließkommazahl (32-Bit)
double
eine beliebige Fließkommazahl (64-Bit)
weitere Zahlenwerte:
Auf folgende weitere Zahlentypen soll hier nicht eingegangen werden: binary, int, negativeInteger, nonNegativeInteger, nonPositiveInteger, positiveInteger, short, unsignedByte, unsigndInt, unsignedLong, unsignedShort
2.11.3 Einschränkungen der Standardwerte Ein XML-Dokument hat beispielsweise folgende Gestalt: 304 E.T.A. Hoffmann Die Elixiere des Teufels Roman 14.00 true
Dann könnte das zugehörige Schema folgendermaßen aussehen:
76
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Dann kann vorgegeben werden, wie oft ein Element vorkommen darf oder muss. Dafür sind die beiden Attribute „minOccurs“ und „maxOccurs“ zuständig. Beispielsweise so:
2.11.4 Mehrere Elemente Ganz korrekt war das bisher Beschriebene nicht. Da es sich im obigen Beispiel um ein Wurzelement mit mehreren Kindelementen handelt, hätte man das Schema wie folgt aufbauen müssen:
Der Unterschied zum oben beschriebenen Schema besteht in dem Element
Die beiden XML-Elemente „xs:complexType“ und „xs:sequence“ legen fest, welche Elemente vorkommen, wie oft sie auftauchen und ob sie zwingend auftauchen müssen. Ihnen kann das Attribut „minOccurs“ mit den beiden sinnvollen Werten „0“ und „1“ zugewiesen werden. Das Attribut „maxOccurs“ macht Sinn mit dem Wert „1“, einer beliebigen
6FKHPDWD
77
Sandini Bib
Zahl > 1 oder dem Wert „unbound“, das heißt beliebig oft. Also beispielsweise so:
Man hätte das Schema auch folgendermaßen aufbauen können:
Das Dokument wird nun geändert. Es tauchen mehrere Gruppen auf. 304 E.T.A. Hoffmann Die Elixiere des Teufels Roman phantastischer Roman Belletristik 14.00 true
Also wird das Schema modifiziert:
78
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
2.11.5 Auswahlliste Tauchen mehrere Kindelemente in einem Dokument auf: 304 E.T.A. Hoffmann Die Elixiere des Teufels Roman Phantasie Belletristik 14.00 true 168 E.T.A. Hoffmann Lebensansichten des Katers Murrs nebst fragmentarischer Biographie des Kapellmeisters Johannes Kreisler in zufälligen Makulaturblättern Roman Satire Belletristik 16.00 true
Nun soll das Schema so modifiziert werden, dass für die Gruppen eine bestimmte Auswahlliste vorgegeben ist, aus denen sie ihre Werte beziehen müssen. Dann könnte das Schema folgendermaßen aussehen:
6FKHPDWD
79
Sandini Bib
Dabei sind nun einige Dinge neu. Zu Beginn wird ein benutzerdefiniertes Element erstellt:
80
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Es erhält den Namen „ArtikelType“. Im zweiten Schritt wird erklärt, welche Elemente „ArtikelType“ beinhaltet: ...
Auffällig bei der Festlegung der Datentypen der Elemente ist, dass „Gruppe“ nun eine Aufzählung besitzt:
Sie werden eingeschlossen in und . Danach wird festgelegt, welche Werte das Element annehmen darf. Dafür stehen einige weitere Eigenschaften fest: Eigenschaft
Beschreibung
xs:enumeration
Werte werden vorgegeben.
xs:length
die Anzahl der möglichen Zeichen
xs:minLength
die Mindestanzahl der Zeichen
xs:maxLength
die Höchstzahl der Zeichen
xs:pattern
Muster für die Eingabe
xs:whiteSpace
der Wert „preserve“ bedeutet, dass Leerzeichen nicht verändert werden. „replace“ sorgt dafür, dass Tabulatoren, Absatzzeichen und Zeilenschaltungen durch Leerzeichen ersetzt werden. „collapse“ ersetzt mehrere aufeinander folgende Leerzeichen durch ein einzelnes.
xs:maxInclusive xs:maxExclusice
der maximale Wert, der angenommen werden kann
xs:minInclusive xs:minExclusive
der minimale Wert, der angenommen werden kann
xs:totalDigits
die maximale Anzahl der möglichen Ziffern einer Zahl
xs:fractionDigits
die maximale Anzahl der Ziffern einer Zahl hinter dem Komma
Tabelle 2.5: Die Dateieigenschaften für xs:simpleType
6FKHPDWD
81
Sandini Bib
2.11.6 Kommentare Über Kommentare gibt es nicht viel zu sagen. Sie werden natürlich nicht im Schema deklariert. Kommentare dürfen zwar nicht am Anfang eines Dokuments stehen, sind aber an jeder beliebigen Stelle zwischen Tags im Schema erlaubt.
2.11.7 Attributdeklaration Natürlich können und müssen auch die Attribute deklariert werden. Das Muster sieht folgendermaßen aus: use="Verwendung"/> default="Attributvorgabewert"/> fixed="fester Attributwert"/>
Für das Attribut „use“ stehen drei Varianten zur Verfügung: „prohibited“ (das Attribut darf nicht verwendet werden), „optional“ (das Attribut darf, muss aber nicht verwendet werden) und „required“ (das Attribut muss verwendet werden). Auch wenn das Schema nicht validiert wird, so soll das letzte Beispiel dennoch hier gezeigt werden:
Abbildung 2.23: Eine XML-Datei mit Schema
82
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
2.12 Validierung Vielleicht haben Sie sich beim Lesen schon die Frage gestellt, welche Funktion und Bedeutung dieses Kapitel hat. Das Thema Auslagern von Teilen, Entitätsreferenzen und Namensräume kann ja ganz praktisch sein. Aber eine DTD? Und ein Schema? Die Daten werden in einen Texteditor eingegeben – der Internet Explorer überprüft auf Wohlgeformtheit, aber nicht auf Gültigkeit. Warum sollte ich mir die Mühe machen, ein gültiges Dokument zu erzeugen (von dem ich noch nicht einmal sicher sagen kann, dass es überhaupt gültig ist), wenn es keiner überprüft? Für die Beantwortung der Frage nach dem Sinn muss man noch einen Schritt weiter gehen. Per Programmierung kann die Gültigkeit überprüft werden. Da XML-Dokumente von Menschen gelesen und geschrieben werden können, könnte man einfache Dateien Anwendern zur Verfügung stellen. Diese werden nach Änderungen veröffentlicht. Was passiert nun aber, wenn ein Benutzer zwar im Sinne der Wohlgeformtheit die Tags korrekt eingegeben hat, diese aber der DTD widersprechen, weil sie so nicht erwähnt werden? Ich denke dabei an die lästigen Tippfehler. Beispielsweise existiert folgendes einfaches Dokument: ]> Twain Marc Melville Hermann
Es wird um eine weitere Autorin erweitert: ... Brown Rita Mae
9DOLGLHUXQJ
83
Sandini Bib
Während der Internet Explorer das Dokument korrekt anzeigt, ist es falsch, weil der falsch geschriebene Tag „Zunahme“ nicht deklariert wurde. Vor der Veröffentlichung überprüft ein mit VBA, VB oder Java geschriebenes Programm das Dokument auf Gültigkeit. Und dann wird erkannt, dass es nicht gültig ist. Wie dies funktioniert, wird in Kapitel 6 beschrieben. Es existiert ein zweiter, einfacherer Weg. Statt selbst Hand anzulegen und zu programmieren, kann auch einer der inzwischen zahlreich vorhandenen XML-Editoren verwendet werden. Im folgenden Kapitel werden einige Editoren genannt, kurz beschrieben und dann gezeigt, wie dort ein XML-Dokument auf Gültigkeit validiert werden kann.
2.13 XML-Editoren 2.13.1 EditML Von der Web-Seite http://www.editml.com/ kann der Editor EditML heruntergeladen werden. Beim Schreiben des Buches stand er in der Version 2.2 zur Verfügung, weitere Versionen sind geplant. Einige Funktionen sind auf 30 Tage begrenzt. Sie finden EditML auch auf der beiligenden CD. Hinter seinen vier Registerblättern werden eine baumartige Ansicht, Quellcode, Browservorschau und eine externe DTD (falls vorhanden) gezeigt. Veränderungen können auf den ersten beiden Blättern vorgenommen werden. Schon beim Laden wird Wohlgeformtheit überprüft. Ist das XML-Dokument nicht wohlgeformt, dann erfolgt eine Fehlermeldung. Über den Menüpunkt VALIDATE / VALIDATE XML wird die Gültigkeit überprüft. Wird ein Fehler gefunden, so wird dies gemeldet.
Abbildung 2.24: Das obige Dokument wird mit EditML validiert
84
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
2.13.2 XML Spy Sehr viel mächtiger und komplexer ist das Tool XML Spy. Es kann als 30Tage-Probeversion von http://www.xmlspy.com/ heruntergeladen werden. Die gezippte Datei hat eine Größe von 18 MByte. Um den Editor verwenden zu können, muss man sich registrieren lassen. Sie finden XMLSpy auch auf der beiliegenden CD. Das Programm besteht aus einer Vielzahl von Fenstern, in denen alle wichtigen Informationen des geladenen oder erstellten XML-Dokuments angezeigt werden. Es werden nicht nur DTDs, sondern auch Schemata unterstützt. Schon beim Öffnen des obigen Dokuments kommt es zu einer Fehlermeldung.
Abbildung 2.25: Der Fehler wird gefunden und angezeigt
Wird der Fehler parallel in einem anderen Editor behoben und werden die Änderungen gespeichert, dann erkennt XML Spy dies sofort und fragt, ob die Datei neu geladen werden soll. Neben einer Reihe von verschiedenen Ansichten auf das Dokument kann auch eine DTD oder ein Schema erzeugt werden. Übrigens liefert das Programm einige interessante Beispiele, an denen schön die Unterschiede zwischen wohlgeformten, gültigen (DTD und Schema) und formatierten XML-Dateien gezeigt werden können. Die einzelnen Elemente werden in den Fenstern dargestellt und können dort verändert werden.
;0/(GLWRUHQ
85
Sandini Bib
Abbildung 2.26: Eine mitgelieferte Beispieldatei als Quellcode
2.13.3 MarkupKit (für Word) Ein hübsches Tool, das als Demoversion von der Webseite http:// www.schema.de/sitehtml/site-d/htmlexpo.htm heruntergeladen werden darf, kommt aus dem Hause SCHEMA. Es installiert im Startup-Verzeichnis von Word eine Dokumentvorlage, an die eine Symbolleiste gebunden ist. Mit dem ersten Symbol kann eine Word-Datei in ein XMLDokument abgespeichert werden. Sie finden MarkupKit auch auf der beiliegenden CD. Das Ergebnis sieht passabel aus. Es kann im Internet Explorer betrachtet werden.
86
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Abbildung 2.27: Aus Word heraus können Dokumente in XML-Code transformiert werden
Abbildung 2.28: Die XML-Datei
;0/(GLWRUHQ
87
Sandini Bib
2.13.4 Weitere Editoren Dies ist selbstverständlich nur eine kleine Auswahl an Editoren. An dieser Stelle sei noch Arbortext genannt, über den Informationen auf der Seite http://www.arbortext.com zur Verfügung stehen. Weitere Editoren können Sie probeweise von folgenden Seiten herunterladen: http://www.meybohm.de (siehe auch beiliegende CD) http://www.tetrasix.com http://www.xemacs.org http://xmetal.com http://morphon.com
2.14 Zusammenfassung Sie wissen nun nach der Lektüre dieses Kapitels, was eine DTD ist. Es wurde erläutert, dass eine DTD innerhalb eines XML-Dokuments oder außerhalb liegen kann. Eine DTD beschreibt vollständig die vorkommenden Elemente und Attribute. Als Alternative zu einer DTD existiert das Schema. Auch ein Schema kann intern oder extern sein. Schemata sind sehr viel komplexer als DTDs, das heißt, sie beschreiben differenzierter die Elemente und Attribute. Wird eine DTD oder ein Schema verwendet, dann kann die Datei auf Gültigkeit überprüft, das heißt validiert werden. Dafür stehen Ihnen XML-Editoren zur Verfügung. In diesem Kapitel wurden einige von ihnen beschrieben.
2.15 Fragen zu Kapitel 2 Allgemeine Fragen Welche der folgenden Aussagen ist falsch? 1. Namensräume (Namespaces) stellen sicher, dass gleichlautende Tags
nicht miteinander in Konflikte geraten. 2. Es existieren fünf Entitätsreferenzen: &, <, > ' und
". 3. Neben diesen vordefinierten Entitätsreferenzen können keine weite-
ren Entiätsreferenzen erstellt werden. 4. Das abschließende Semikolon ist – ebenso wie in HTML – nicht zwin-
gend notwendig. 5. Attributwerte können, müssen aber nicht, in Anführungszeichen ge-
setzt werden. 6. Ein Tag kann beliebig viele Attribute besitzen.
88
:RKOJHIRUPWH ;0/'RNXPHQWH
Sandini Bib
Zur DTD 1. Welche der folgenden Dokumente können nicht durch folgende
DTD beschrieben werden? • • • • • • 2. Welche DTD beschreibt das folgende Dokument?
Fehler Im folgenden Dokument sind vier Fehler. Welche? Pyramiden von Gizeh Pharos von Alexandria Zeusstatue von Olympia Artemistempel in Ephesos Mausoleum in Halikarnassos Koloss von Rhodos Haengende Gaerten der Semiramis
)UDJHQ ]X .DSLWHO
89
Sandini Bib
Sandini Bib
3
XML, HTML und XSL – die Ausgabe
le
n e rn
Nun, nachdem eine wohlgeformte oder eine gültige XML-Datei erstellt worden ist, stellt sich die Frage, was mit ihr geschieht. Die Anzeige im Internet Explorer ist zur reinen Informationsvermittlung okay, allerdings wenig brauchbar, da die Benutzer von Browsern anderes gewöhnt sind. Die Daten sollen auf vernünftige, das heißt ansehnliche Weise veröffentlicht werden. Und genau hier greift HTML. Eine einfache Möglichkeit, XML-Dokumente anzuzeigen, sind Style Sheets
3.1
Style Sheets
So wie Dokumente in Textverarbeitungsprogrammen nicht „hart“, sondern mit Formatvorlagen formatiert werden sollten, stellen viele Browser die Möglichkeit zur Verfügung, mit CSS (Cascading Style Sheets) zu arbeiten. Diese Technik wurde vom World Wide Web Consortium 1996 entwickelt und im Netscape 4.0 und Internet Explorer 4.0 vollständig implementiert. Der dahinter liegende Grundgedanke ist die Trennung von Information und Gestaltung. CSS ist lediglich für das Aussehen eines Dokumentes zuständig; in ihm sind keinerlei Informationen über die Struktur des Dokuments verzeichnet. Verzichtet man auf CSS, kann ein aufgeblähter und unübersichtlicher Code entstehen. Mit CSS können kürzere Ladezeiten erreicht werden. Und schließlich ist die Pflege der Formate leichter, wenn CSS verwendet wird. Ein CSS-Dokument ist eine Datei, in der die Stile für andere HTMLSeiten festgelegt werden. Sie wird als Textdatei mit der Endung *.css abgespeichert und könnte beispielsweise folgende Gestalt haben:
Wird diese Stilvorlage von einem anderen Dokument verwendet, so wird in diese der Befehl
eingefügt. Dieser Befehl wird im Kopfteil (HEAD) untergebracht. Wie das obige Beispiel verdeutlicht, besteht ein CSS aus zwei Teilen: einem Selektor, beispielsweise H1, und der Deklaration, die in geschweiften Klammern geschrieben wird. Die Deklaration wiederum besteht aus zwei Teilen: den Eigenschaften, wie zum Beispiel Größe und Farbe, und dem Wert (Tag), beispielsweise 14 pt und Rot. Solche Deklarationen können vererbt werden (deshalb der Name „cascading“). Wird beispielsweise für das -Tag die Farbe per CSS bestimmt, hat der
-Tag automatisch die gleiche Farbe, wenn diese dort nicht definiert ist. Selbstverständlich könnten nun direkt im Quellcode Befehle eingegeben werden, die vom vorgegebenen Design abweichen: Überschrift
Die folgende Tabelle liefert die wichtigsten CSS-Befehle:
92
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib Befehl
Bedeutung
weitere mögliche Angaben
font-family
Schriftart
Arial, Times New Roman etc.
font-size
Schriftgröße
numerischer Wert in pt (Punkt), mm oder cm
color
Schriftfarbe
red, green, white usw. oder HTML Farbangabe
background-color
Hintergrundfarbe
font-variant
Schriftvariante
Schrift
normal, small-caps
font-weight
Schriftgewicht
normal, bold, bolder, lighter
font-style
Schriftstil
normal, oblique, italic
float
Position des umfließenden Textes
left, right oder none, wenn Text ein Element umfließen soll
text-align
Textausrichtung
left, right, center, justify (Blocksatz)
line-height
Zeilenabstand (Durchschuss)
numerischer Wert in pt (Punkt), mm oder cm
text-decoration
Textgestaltung
underline, overline, linethrough, blink
word-spacing
Wortabstand
numerischer Wert in pt (Punkt), mm oder cm
letter-spacing
Zeichenabstand
numerischer Wert in pt (Punkt), mm oder cm
text-indent
Texteinrückung
numerischer Wert in pt (Punkt), mm oder cm
text-transform
Textart
capitalize, uppercase, lowercase, none
vertical-align
vertikale Textausrichtung
baseline, sub, super, top, texttop, middle, bottom oder textbottom
Und sehr viel mehr gibt es zu XML auch nicht zu sagen. Die XML-Datei sieht folgendermaßen aus: ]>
94
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib 12039 Wie man mit einem Lachs verreist Unterhaltung 20048 Baudolino Unterhaltung
Über den Tag
wird das Stylesheet „RenesFormate02.css“ eingebunden. Diese Zeile wird auch „Verarbeitungsanweisung“ („Processing Instruction“) genannt. Der Speicherordner ist der gleiche wie derjenige, in dem sich die XML-Datei befindet. Er kann absolut oder relativ sein (wie in diesem Fall). Mit dem type ist die Art des Stylesheets gemeint – "text/css" entspricht CSS. Während in HTML ein internes Stylesheet möglich ist (vergleichbar mit internen DTD bei XML-Dokumenten), so muss bei XML das Stylesheet außerhalb liegen. Die CSS-Datei „RenesFormate02.css“ sieht dann folgendermaßen aus: Nummer { font-family: Arial; font-size: 40pt; color: FF0000; font-weight: bold; margin-top: 0; margin-bottom: 6pt } Titel { font-family: Times; font-size: 14pt; color:blue; font-weight: bold; } Gruppe { font-family: Times; font-size: 12pt; list-style-type: disc }
Abbildung 3.1: An das XML-Dokument wird eine css-Datei gebunden. Damit kann die Ausgabe gestaltet werden.
Im Unterschied zur CSS-Datei, die in HTML verwendet wird, müssen in HTML die Stilklassen von HTML verwendet werden. In XML werden eigene Stilklassen benutzt: die Namen der XML-Elemente. Die Formatierungen sind die gleichen wie in HTML, wie sie in obiger Tabelle beschrieben worden sind. Will man die einzelnen Elemente untereinander stellen und nicht als Fließtext hintereinander laufen lassen, so muss das Attribut display: block;
hinzugefügt werden. Die css-Datei sieht dann wie folgt aus: Nummer { display: block; font-family: Arial; font-size: 40pt; color: FF0000; font-weight: bold;
Es können auch mehrere Selektoren gruppiert werden. Dazu werden sie durch ein Komma getrennt, hintereinander aufgelistet: Nummer { display: block; font-family: Arial; font-size: 40pt; color: FF0000; font-weight: bold; margin-top: 0; margin-bottom: 6pt } Titel, Gruppe { font-family: Times; font-size: 14pt; color:blue; font-weight: bold; } Preis
Abbildung 3.2: Das gleiche Dokument – anders gestaltet
3.1.1 Farben In den letzten Beispielen wurde sowohl die Farbe „red“ als auch „FF0000“ verwendet. Es stellt sich die Frage, welche Farben zur Verfügung stehen und wie sie ausgedrückt werden. Alle Farben, die im Internet dargestellt werden, setzen sich aus den drei Grundfarben Rot, Grün und Blau zusammen. Dieses additive Farbmodell wird überall bei Ausgabequellen mit Licht verwendet: Bildschirm, Beamer, LCD-Display, ... Dieses Modell wird RGB-Farbmodell genannt: red, green und blue. Jede Farbe kann einen Wert zwischen 0 und 255 annehmen. Nimmt man den höchsten Wert, erhält man die reine Farbe. So ist Rot dann 255 rot, 0 grün, 0 blau. Weiß ist 255, 255, 255 und Schwarz das Gegenteil 0, 0, 0. Eine Farbe, hier Rot, wird in der Regel wie folgt angegeben: color="#FF0000"
98
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
Eigentlich müsste doch eine neunstellige Zahl dastehen. Allerdings werden die Werte hexadezimal angegeben. Jedoch unterstützt jedes bessere HTML-Tool die Umwandlung. In den meisten HTML-Editoren können Sie direkt aus den Farben auswählen und bekommen dann die Hexadezimalzahlen. Dezimal
0
1
2
...
9 10
11
12
13
14
15
16
17
18
...
255
Hexadezimal
0
1
2
...
9 A
B
C
D
E
F
10
11
12
...
FF
Der Aufbau der Farbnummer ist im Hexadezimalsystem immer wie folgt: #000000 Die ersten beiden Ziffern stehen für den Rot-Anteil, die nächsten für den Grün-Anteil und die hinteren beiden für den Blau-Anteil. Farben können auch über ihre englischen Namen angegeben werden. Achtung: Obwohl Browser eine große Anzahl von vordefinierten Farbnamen unterstützen, können in einer css-Datei lediglich 16 Farben verwendet werden: Farbname
Bedeutung
Farbname
Bedeutung
Aqua
Blaugrün
Navy
Marineblau
Black
Schwarz
Olive
Olivgrün
Blue
Blau
Purple
Violett
Fuchsia
Fuchsin
Red
Rot
Gray
Grau
Silver
Silber
Green
Grün
Teal
Braun
Lime
Zitronengelb
White
Weiß
Maroon
Kastanienbraun
Yellow
Gelb
Tabelle 3.2: Die vom CSS-Standard unterstützten Farben
Die nächste Abbildung zeigt einen HTML-Editor, bei welchem lediglich eine Farbe angeklickt werden muss. Der Editor berechnet den zugrunde liegenden Farbwert und fügt ihn in das HTML-Dokument ein. Die folgende Abbildung zeigt den HTML-Editor Phase 5, der kostenlos von der Seite http://www.meybohm.de heruntergeladen werden darf.
6W\OH 6KHHWV
99
Sandini Bib
Abbildung 3.3: Viele HTML-Editoren helfen bei der Umwandlung der Farbe in den hexadezimalen Code (hier: rechts außen)
3.1.2 Klassen Sollen nicht alle Elemente einer Liste gleich gestaltet werden, so können sie mit Hilfe des Attributs CLASS formatiert werden. Von der oben verwendeten css-Datei werden die Elemente Titel und Gruppe um zwei Klassen („Rot“ und „Schwarz“) erweitert: Nummer { display: block; font-family: Arial; font-size: 40pt; color: FF0000; font-weight: bold; margin-top: 0; margin-bottom: 6pt } Titel, Gruppe { font-family: Times; font-size: 14pt; font-weight: bold; } .Blau { color:blue; } .Schwarz { color:black; }
100
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
In das Tag werden nun das Attribut CLASS und seine Werte („Blau“ oder „Schwarz“) eingefügt: 12039 Wie man mit einem Lachs verreist Unterhaltung 20048 Baudolino Unterhaltung
Wenn Sie mit einer DTD arbeiten, so beachten Sie, dass Sie diese ebenfalls erweitern müssen: ]>
Die Regeln zum Erstellen einer Klasse sind offensichtlich: In der cssDatei wird vor den Namen ein Punkt gestellt. Sie gehört zu der Klasse, die ihr direkt vorangestellt ist. Das Attribut CLASS kann nun in den entsprechenden Elementen verwendet werden, muss allerdings in der DTD deklariert werden.
6W\OH 6KHHWV
101
Sandini Bib
Abbildung 3.4: Das Dokument wird mit Klassen gestaltet
3.1.3 Bilder Befinden sich im XML-Dokument Bilder, das heißt existiert dort ein Tag , so muss dieser mit dem Attribut versehen werden, welches die Quelle des Bildes angibt: 123 Wolff Autobiografie 124 Bodychek Ratgeber
Der Namensraum wird selbstverständlich im Wurzelelement deklariert:
In den Processing-Instructions wird erneut auf eine css-Datei verwiesen:
102
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
Dort wird deklariert: Nummer { display: block; font-family: Arial; font-size: 40pt; color: FF0000; font-weight: bold; margin-top: 0; margin-bottom: 6pt } Titel, Gruppe { font-family: Times; font-size: 14pt; font-weight: bold; position: relative; top: -55 }
Die Veränderung der Position war nötig, damit der Text ordentlich neben den Bildern zu stehen kommt.
Abbildung 3.5: Ein XML-Dokument mit Bildern
6W\OH 6KHHWV
103
Sandini Bib
3.1.4 Hyperlinks Um einen Hyperlink in einer XML-Datei zu erstellen existieren verschiedene Möglichkeiten. Eine sehr einfache Variante besteht darin – wie bei den Bildern –, einen HTML-Namensraum zu definieren und anschließend den HTML-Tag mit dem Attribut HREF zu verwenden. Beispielsweise so: ... Mehr Informationen
Natürlich könnte man das Tag in der css-Datei formatieren. Dies ist im eben beschriebenen Beispiel nicht getan worden:
Abbildung 3.6: XML-Seite mit Hyperlink
3.2
Einbetten von XML-Daten in ein HTMLDokument
Bislang haben wir so getan, als würden uns nur die Daten interessieren. Stellen Sie sich die vielen Datenbanken, Listen und Aufzählungen vor,
104
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
wenn sie singulär auf Web-Seiten lägen. Damit auf einer Seite HTMLElemente wie Bilder, Tabellen oder Ähnliches verwendet werden können, muss das Dokument folgende Gestalt haben: Nehmen wir ein abgewandeltes Beispiel aus dem letzten Kapitel. Das XML-Dokument sieht wie folgt aus: Zuerst ein Buch Der Kaufmann von Venedig William Shakespeare Anschließend eine CD Zuckerschlampen Rosenstolz Und zum Schluss eine DVD The Jungle Book Walt Disney
Die css-Datei („KunterbuntFormate.css“) spielt keine wichtige Rolle – doch aus Gründen der Vollständigkeit soll sie hier gezeigt werden: Nummer { font-family: Arial; font-size: 40pt; color: FF0000; font-weight: bold; margin-top: 0; margin-bottom: 6pt } Titel { font-family: Times; font-size: 14pt; color:blue; font-weight: bold; } Autor
Das Besondere an diesem Dokument ist das Verwenden des HTML-Befehls H1. Allerdings wird er nicht wie bei einem HTML-Dokument geschrieben, sondern mit der Namensraumdefinition „html:“. Damit wird dieser Tag nicht als irgendein beliebiges XML-Element interpretiert, sondern als fest definiertes HTML-Tag. Und: damit der Browser den Namen – oder genauer: den Namensraum – „html:h1“ korrekt interpretiert, wird dem Wurzelelement „Liste“ das Attribut HYPERLINK "http://www.w3.org/TR/REC-html40" in Form von
hinzugefügt. Hinter dieser Web-Adresse verbirgt sich die HTML 4.0 Spezifikation. Mit Hilfe dieser eindeutigen Adresse wird der Raum „html“ exakt definiert. Die Seite wird nicht geladen – auch wenn Sie offline dieses Dokument testen, sehen Sie die korrekte Ansicht. Übrigens hätte man auch irgendeine beliebige URL verwenden können – die Anzeige wäre korrekt gewesen. Wie wäre es beispielsweise mit der Adresse meiner Web-Seite:
Damit ist eigentlich klar, wie ein HTML-Dokument strukturiert sein muss, damit es sowohl HTML-Befehle als auch XML-Elemente verwenden kann. Schauen Sie sich bitte folgendes Dokument an: Buch Nummer 1 VBA mit Word 2002 3-8273-1897-1 400 24,95 Buch Nummer 2 Workshop VBA 3-8273-1663-4 370 24,95 Buch Nummer 3 Visio 2000 professionell 3-8273-1626-X 540 39,95
(LQEHWWHQ YRQ ;0/'DWHQ LQ HLQ +70/'RNXPHQW
107
Sandini Bib
Zwei Dinge fallen dabei auf: HTML-Tags werden nun genauso geschrieben wie XML-Tags. Auch wenn es in HTML nicht nötig wäre, werden sie hier geschachtelt. Jedes Start-Tag besitzt ein Endtag: Buch Nummer 1
Tags wie das IMG-Tag, welche eigentlich kein Schluss-Tag benötigen, werden mit einem Schrägstrich am Ende versehen um anzuzeigen, dass es sich um ein „leeres“ Tag handelt. Natürlich ist es nicht leer – schließlich interessiert uns das Attribut „scr“, welches das Bild darstellt. Das zweite, was auffällt, ist der Code selbst. Es kann doch wohl nicht sein, dass jedes einzelne Buch aufgelistet im Code steht. Bei einer Liste von mehreren Hundert Büchern wäre dies eine riesige Aufgabe. Dazu sollten Sie XSL verwenden. Es wird im nächsten Kapitel beschrieben.
Abbildung 3.8: Zu viel Code für die Darstellung
108
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
3.3
XSL
Eine weitere und viel elegantere Möglichkeit, die Daten auszugeben und zu formatieren, besteht über XSL (Extensible Stylesheet Language). Während XML die Daten hält und strukturiert, gibt XSL sie aus. Genauer gesagt besteht XSL sogar aus zwei Teilen: der eine transformiert die Daten, der andere formatiert sie. Beide Teile können unabhängig voneinander agieren, machen in der Regel aber erst im Zusammenspiel Sinn. Die Transformationssprache wird häufig XSLT genannt.
3.3.1 Der Aufbau einer XSL-Datei Schauen wir uns ein Beispiel an. Eine XSL-Datei hat folgende Gestalt:
Sie wird durch das Tag eingeleitet. Als Attribut wird der Namensraum definiert (xmlns:xsl="http://www.w3.org/TR/WD-xsl"). Dieses Tag findet am Ende des Dokuments seine Entsprechung. Das nächste Tag definiert den Namensraum. Nach dem Doppelpunkt folgt das Schlüsselwort template. Auch dieses Tag findet seine Entsprechung am Ende. Das folgende Tag ist nun die erste Formatierung („Heading1“), in welchem sich das Element „Nummer“ befindet, oder genauer: "Artikel/Buch/Nummer". „Artikel“ ist das Wurzelelement und hat selbst keine Inhalte, kann deshalb folglich auch nicht formatiert werden. Das Attribut value-of liest den Inhalt aus. Das zweite Tag
formatiert die übrigen zwei Elemente und stellt sie im Browser dar. Diese XSL-Datei muss nun noch in die XML-Datei eingebunden werden. Dies geschieht sehr ähnlich wie bei einer CSS-Datei mit dem Befehl:
;6/
109
Sandini Bib
Auch hier steht diese Zeile direkt nach
Wie man an diesem Beispiel sehen kann, wird die XSL-Datei mit der Endung „xsl“ gespeichert. Man kann sich diese Datei übrigens auch im Browser anzeigen lassen – die Anzeige entspricht einem XML-Dokument ohne Formatierung.
Abbildung 3.9: Das XML-Dokument wird mit einer XSL-Datei formatiert
Neben dem Kindelement xsl:template stehen noch weitere Elemente zur Verfügung. Auf sie soll nicht weiter eingegangen werden. Hier die vollständige Liste: Element
Beschreibung
xsl:import
Ein XSLT-Stylesheet wird importiert.
xsl:include
Ein XSLT-Stylesheet wird eingebunden.
xsl:strip-space
Tauchen mehrere Leerzeichen im Dokument auf, werden sie entfernt.
xsl:preserve-space
Tauchen mehrere Leerzeichen im Dokument auf, bleiben sie erhalten.
xsl:output
Damit kann ein XML-Dokument in ein anderes XML-, ein HTML-Dokument oder eine Textdatei verwandelt werden.
xsl:key
Schlüssel
Tabelle 3.3: Die Kindelemente von
110
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib Element
Beschreibung
xsl:decimal-format
Bestimmt das Aussehen von reellen Zahlen
xsl:namespacealias
Das Namensraumpräfix wird durch ein anderes Präfix ersetzt.
xsl:attribute-set
Attribute können erzeugt werden.
xsl-variable
Eine Variable wird deklariert.
xsl:param
Eine globale Variable wird deklariert.
xls:template
Damit wird der Knoten bereitgestellt, mit dem das XSLT-Dokument bearbeitet wird.
Tabelle 3.3: Die Kindelemente von (Forts.)
3.3.2 Mehrere Kindelemente anzeigen Wenn nun allerdings mehrere Elemente in dem XML-Dokument stehen, so muss die XSL-Datei modifiziert werden. Vom Dokument 117 Biologisch kochen und backen Kochbuch 14,90 118 Meine Rezepte Kochbuch 44,90 119 Original italienische Pasta Kochbuch 38,80
wird unverändert nur das erste Element angezeigt. Damit alle Einträge zu sehen sind, muss das XSL-Dokument mit for-each modifiziert werden:
;6/
111
Sandini Bib
Zwei Änderungen wurden vorgenommen. Zum einen wurde ein foreach-Tag eingeführt, der alle Elemente von durchläuft. Nun muss mit dem select-Attribut nicht mehr der gesamte Pfad angegeben werden, sondern nun genügt der Name des Elements. Da sich die drei Elemente „Nummer“. „Titel“ und „Buch“ auf der gleichen Ebene befinden, können sie problemlos in die for-each-Schleife eingebunden werden.
Abbildung 3.10: Mehrere Elemente werden angezeigt
Selbstverständlich müssen nicht alle Elemente angezeigt werden. Man könnte XSL auch zur Selektion von Daten heranziehen. Im folgenden Beispiel werden lediglich die Titel angezeigt:
112
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
Abbildung 3.11: Es kann auch nur ein Teil angezeigt werden
Dabei können die wichtigsten HTML-Tags verwendet werden: , , ... ,
, , , , , <STRONG>, , , , , , und noch einige weitere. Und damit werden auch die Vorteile der Verwendung von XML und XSL deutlich. Es geht nicht nur um den alleinigen Import und Export von Daten, sondern sie sollen zugleich auf eine bestimmte Art angezeigt werden. Gerade bei Änderungen wirkt sich die Trennung in Gestaltung und Daten positiv aus. Die Daten können leicht verändert oder neu eingelesen werden, die Formatierung kann leicht geändert werden. Ebenso kann ein Element schnell von einer Formatierungsebene auf eine andere geschoben werden.
3.3.3 Alle Elemente einer Ebene anzeigen Sollen alle Elemente angezeigt werden, ganz gleich wie sie heißen, dann kann die Selektion mit folgendem Operator durchgeführt werden:
;6/
113
Sandini Bib
3.3.4 Attribute auslesen Befinden sich Werte in Attributen, so können auch diese ausgelesen werden. Im folgenden Dokument besitzt das Element das Attribut „Preis“: 117 Biologisch kochen und backen Kochbuch 118 Meine Rezepte Kochbuch 119 Original italienische Pasta Kochbuch
Das zugehörige XSL-Dokument liest alle Elemente der Kindknoten „Buch“ und sein Attribut „Preis“ aus:
Beachten Sie, dass sich vor dem Attributnamen ein „@“ befindet. Und beachten Sie, dass die Zeile
alle Elemente der Ebene „Artikel/Buch“ herausholt.
114
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
Abbildung 3.12: Auch Attribute können ausgelesen werden
3.3.5 STYLE in SPAN und DIV Noch flexibler gestaltet sich die Ausgabe, wenn zu den Tags das STYLEAttribute verwendet wird. Insbesondere die beiden Tags und eignen sich dafür, da sie nicht vorbelegt sind, sondern als Attribute die einzelnen Gestaltungsmerkmale erhalten. Sie wurden mit HTML 4.0 eingeführt.
Die beiden ursprünglichen Tags und
wurden ersetzt durch . Als Attribut STYLE erhält er die Schriftmerkmale zugewiesen. Da sich und innerhalb eines -Tags befinden, werden sie
;6/
115
Sandini Bib
hintereinander geschrieben. Wollte man dies verhindern, müsste man nach enden lassen:
Abbildung 3.13: Unterschiedliche Gestaltungen mit XSL
Das Tag erzeugt einen automatischen Zeilenumbruch, der hier gewünscht wird. Sollte er vermieden werden, kann statt das Tag verwendet werden – dort findet kein Umbruch statt:
116
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
Abbildung 3.14: Das gleiche Dokument mit dem Tag statt
Normalerweise wird für eine Absatzgestaltung verwendet, innerhalb eines Fließtexts für Zeichenformatierung. Will man einen bestimmten Abstand zwischen den einzelnen Absätzen definieren, so kann der Abstand festgelegt werden:
;6/
117
Sandini Bib
Abbildung 3.15: Der Text mit Einrückungen
3.3.6 Tabellen Aber um längere Texte soll es in diesem Buch nicht gehen. Vielmehr stehen Daten im Vordergrund, die ordentlich dargestellt werden sollen. Und dafür stellt HTML bekanntlich Tabellen zur Verfügung.
118
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
Nummer
Titel
Gruppe
In diesem langen Beispiel stecken nun einige Informationen. Verwendet wurden einige typische HTML-Formate, wie
;6/
119
Sandini Bib
um die Breite der Tabelle (100% der Browserbreite), die Ausrichtung des Kopfs (zentriert) und die Breite der ersten Spalte (25%) festzulegen. Die zentrierte Ausrichtung ist dabei überflüssig – sie wird bereits im Style „Tabellenkopf“ festgelegt: .Tabellenkopf { align:center; color:red; font-family:Arial; font-size:24pt; font-weight:bold }
Dieser Stil wird nun für den Kopf verwendet, ein zweiter für die eigentlichen Daten. Alle Elemente des Tag Artikel/Buch werden durchlaufen.
Danach wird eine Zeile erzeugt (
), in der Zelle wird die Formatierung verwendet und das Element herausgeholt:
Abbildung 3.16: Die Ausgabe als Tabelle
120
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
3.3.7 Daten sortieren Doch XSL hat noch weitere angenehme Eigenschaften. Man kann mit seiner Hilfe die Daten sortieren lassen. Läuft eine Schleife durch alle Datensätze, dann kann man im for-each-Tag bereits festlegen, nach welchem Kriterium die Daten sortiert werden:
Mit Hilfe des Attributs „order-by“ wird in Anführungszeichen die Sortierreihenfolge (+ oder -) festgelegt, also aufsteigend oder absteigend. Danach folgt der Elementname, nach dem sortiert werden soll.
3.3.8 Daten filtern Man kann auch Daten filtern. Leider kann man nicht direkt über ein Kriterium auf den Inhalt eines Elements zugreifen. Aber man kann über Attribute Daten filtern. Angenommen das obige Beispiel wird ein wenig modifiziert und sieht nun folgendermaßen aus: Biologisch kochen und backen Kochbuch 14,90 Meine Rezepte Kochbuch 44,90 Italienische Pasta Kochbuch 38,80
Die Nummern sind nun Attribute der Buch-Tags. Nun kann man ein Buch oder mehrere Bücher herausfiltern: ...
;6/
121
Sandini Bib
Es fällt das Tag auf. In ihm wird das Element angegeben und in eckiger Klammer, getrennt durch einen Klammeraffen, der Name des Attributs. Ein Tag könnte ja mehrere Attribute besitzen. Hinter dem Gleichheitszeichen steht nun der Wert, der gefiltert werden soll. Da match selbst eine Zeichenketten verlangt, die in Anführungszeichen geschrieben ist, muss der Wert (hier 117) in einfachen Hochkommata gesetzt werden. Man könnte ebenso die bekannten Vergleichsoperatoren verwenden
Dass die beiden Tags
beendet werden müssen, ist klar. Sie finden die Schlusstags in:
Ebenso wie WENN-Funktionen in Excel oder IF-Verzweigungen in VBA gibt es auch hier einen Sonst-Zweig. Er wird mit eingeleitet und natürlich mit beendet. Damit könnte man bei-
122
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
spielsweise – in Abhängigkeit von einem Kriterium – eine bestimmte Formatierung einschalten.
Abbildung 3.17: Die Daten werden gefiltert und sortiert
Jedoch Achtung: Da das Attribut im obigen Beispiel keine echte Zahl ist, werden die Attribute wie Zeichenketten verglichen. Das heißt, dass ein Wert ebenso das Kriterium erfüllt, da lediglich die Zeichen von links nach rechts gelesen werden. Und da 9 > 1, folgt: 99 > 117. Dieses Problem, das übrigens auch bei der Sortierung auftritt, könnte einfach umgangen werden, indem die Stellen vor der ersten Ziffer „aufgefüllt“ werden. Also beispielsweise so:
3.3.9 Ebenen durchlaufen Stehen im Zentrum die Daten, dann kann ein Feld dargestellt werden über:
;6/
123
Sandini Bib
Sollen dagegen alle Elemente von „Artikel/Buch“ angezeigt werden, so kann das select-Kriterium modifiziert werden:
Auch der umgekehrte Fall ist möglich. Sind beispielsweise die Bücher gesucht, ist aber nicht klar, auf welcher Ebene das Element „Buch“ steht (möglicherweise sogar auf unterschiedlichen Ebenen), dann kann das Suchkriterium mit zwei Schrägstrichen geschrieben werden als:
Dadurch werden alle Ebenen durchsucht, bis ein Element gefunden wurde. Seine Inhalte können dann mit
angezeigt werden.
Abbildung 3.18: Alle Daten:
124
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
3.3.10 Entscheidungen An einem größeren Beispiel soll gezeigt werden, wie in XML gehaltene Daten durch XSL strukturiert ausgegeben werden. Gegeben sei ein (beliebig) großes Dokument, in dem sich Bücher aus verschiedenen Kategorien befinden: Alle träumten von Cuba Miguel Barnet 1981 Ansichten der Tropen im Morgengrauen Guillermo Cabrera Infante 1992 Drei traurige Tiger Guillermo Cabrera Infante 1987 kolibri Severo Sarduy 1991 Woher die Sänger sind Severo Sarduy 1993 Bevor es Nacht wird Reinaldo Arenas 1993 Rosa Reinaldo Arenas 1984 Reise nach Havanna
;6/
125
Sandini Bib Reinaldo Arenas 1994 Die Plantage Reinaldo Arenas 1981
In einer Tabelle sollen nun – so die Vorgabe – zuerst die Gedichte, dann die Romane und am Ende die Autobiografien angezeigt werden. Die XSL-Datei XSLFormate21.xsl sieht wie folgt aus:
Autor
Titel
126
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib Jahr
Kategorie
;6/
127
Sandini Bib
128
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
Im oberen CDATA-Abschnitt befinden sich die beiden STYLE-Attribute, mit denen das Dokument gestaltet wird. Drei Mal – für jede Kategorie erneut – wird die Schleife for-each bemüht. In ihr wird festgelegt, dass nach Autor sortiert werden soll:
Über das Attribut „Kategorie“ wird zuerst die Kategorie „Gedichte“ gefiltert, anschließend die „Romane“ und am Ende die Kategorie „Autobiografie“. Das Ergebnis stellt sich dann wie folgt dar:
Abbildung 3.19: Die gefilterten und sortierten Daten
3.3.11 Bilder Ähnlich wie mit einer css-Datei können auch mit XSL Bilder angezeigt werden. Angenommen das Dokument hat folgende Gestalt: VBA mit Word 2002 3-8273-1897-1 400 24,95 wordxp.jpg
Das heißt, die Namen der Bilder befinden sich in den Tags . Als solche können sie ausgelesen werden – falls sich die Bilder im gleichen Ordner befinden wie das XML- und das XSL-Dokument. Die zugehörige XSL-Datei „XSLFormate26.xsl“ hat demnach folgende Gestalt: Preis: / Seiten
130
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
Welche Informationen angezeigt werden und wie sie formatiert werden, spielt nun keine Rolle. Interessant ist das Element :
Das Tag ermittelt den Dateinamen und weist ihn dem Attribut „src“ des Tags zu. Damit wird das Bild angezeigt:
Abbildung 3.20: Die Bilder werden korrekt angezeigt
Angenommen, die Bilder befinden sich in einem anderen Ordner. Soll der Ordner absolut angegeben werden, dann muss mit einem gemischten Element gearbeitet werden: C:\Eigene Dateien\XML\Kapitel03\Bilder\
Pfad und Dateiname werden zusammengesetzt, die Bilder werden korrekt angezeigt. Ebenso können die Dateinamen in Attributen gespeichert werden. Beispielsweise könnte das XML-Dokument so aussehen:
;6/
131
Sandini Bib VBA mit Word 2002 3-8273-1897-1 400 24,95 Workshop VBA 3-8273-1663-4 370 24,95 Visio 2000 professionell 3-8273-1626-X 540 39,95
Das Tag ist leer – es enthält lediglich das Attribut „Quelle“ mit dem Dateinamen als Wert. Diesmal wird nicht der Inhalt eines Elements ausgelesen, sondern das Attribut. Und: Der Pfad soll relativ zum Speicherort der XML-Datei angegeben werden – die Bilder befinden sich in einem Unterordner „Bilder“. Literaturliste
132
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
Das Tag wurde oben bereits beschrieben. Nun holt es ein Attribut aus dem Element. Dies wird durch den Klammeraffen gekennzeichnet:
Das Ergebnis sieht dann im Browser folgendermaßen aus:
Abbildung 3.21: Diesmal werden die Dateinamen aus Attributen ausgelesen
;6/
133
Sandini Bib
3.3.12 Hyperlinks Nun dürfte es eigentlich kein Problem mehr darstellen, mit Hilfe von XSL Informationen aus Elementen oder Attributen herauszuholen und als Hyperlink darzustellen. Gegeben sei folgende, abgekürzte Literaturliste: VBA mit Word 2002 3-8273-1897-1 400 24,95 Weitere Informationen …
Jedes Buchelement enthält einen Tag , der die Internet-Adresse des Verlags beinhaltet. Dieser wird in der Datei „XSLFormate28.xsl“ ausgelesen und dargestellt: … Literaturliste
Bilder\
134
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
Das Ergebnis kann sich sehen lassen:
Abbildung 3.22: Die Hyperlinks wurden mit XSL erzeugt
3.4
Zusammenfassung
In diesem Kapitel wurden drei Varianten beschrieben, wie eine XMLDatei dargestellt werden kann. Die erste Möglichkeit wäre durch HTMLTags, die als Namensraum in das Dokument eingelagert werden. Diese Variante ist einfach, wenn man HTML als bekannt voraussetzt, allerdings recht unflexibel.
=XVDPPHQIDVVXQJ
135
Sandini Bib
Die zweite Möglichkeit besteht in der Verknüpfung einer XML-Datei mit einem css-Dokument. Ähnlich wie in HTML werden dort die Formatierungen festgelegt. Nur mit dem Unterschied, dass es sich nun um frei definierte Elementnamen handelt. Die wohl wichtigste Möglichkeit heißt XSL („Extensible Stylesheet Language“). Damit können Daten flexibel aus dem XML-Dokument gefiltert und anschließend sortiert werden, Attributwerte können ausgelesen und dargestellt werden. Gleichzeitig stehen die HTML-Tags zur Formatierung, exakten Positionierung und zur Darstellung von Tabellen zur Verfügung. Eine besondere Rolle spielen die beiden Tags und , da mit ihrer Hilfe Formatierungen frei vergeben werden können.
3.5
Fragen zu Kapitel 3
Frage 1 Gegeben sei folgendes XML-Dokument: Anton Berti Conni Det Edi Fritzchen
Welche der folgenden Darstellungen erhält man, wenn die XSL-Datei „XSLFormateUe01.xsl“ folgende Gestalt hat:
136
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
Abbildung 3.23: Lösung 1
Abbildung 3.24: Lösung 2
)UDJHQ ]X .DSLWHO
137
Sandini Bib
Abbildung 3.25: Lösung 3
Abbildung 3.26: Lösung 4
138
;0/ +70/ XQG ;6/ ¥ GLH $XVJDEH
Sandini Bib
Frage 2 Wie muss die XSL-Datei modifiziert werden, damit die anderen drei Ergebnisse angezeigt werden? Frage 3 Wie verbindet man das Dokument mit einer css-Datei und wie sieht sie aus, damit Lösung 3 und Lösung 4 erzeugt werden?
)UDJHQ ]X .DSLWHO
139
Sandini Bib
Sandini Bib
4
le
Daten in Excel und Access aufbereiten
n e rn
Nachdem nun geklärt wurde, wie gültige und wohlgeformte XMLDokumente erstellt werden, stellt sich die Frage, ob und wie bereits vorhandene Daten in eine XML-Datei transformiert werden können. Zwar können Excel und Access seit der Version 2002 dies direkt, allerdings liefert der Export nicht immer das gewünschte Ergebnis. Bevor wir uns dem DOM zuwenden, soll kurz erläutert werden, wie Fremddaten, die nach Excel oder Access gebracht werden, dort aufbereitet werden können. Dies wird im vorliegenden Kapitel exemplarisch vorgeführt. Kennen Sie das Problem? Sie erhalten von irgendeinem System Daten, die nicht in dem Format vorliegen, in dem Sie es brauchen. Sie exportieren aus SAP eine Namensliste, in der Vor- und Zunamen in einer Spalte stehen. Sie erhalten Daten vom Großrechner, bei dem Punkt und Komma vertauscht sind, also statt 1.234,56 Euro findet sich dort 1,234.56 Euro. In diesem Kapitel wird aufgezeigt, welche Funktionen, Tools und Assistenten Excel und Access zur Verfügung stellen, um solche „problematischen“ Datenbanken zu bereinigen.
4.1
Excel
4.1.1 Die Daten kommen Die Daten, die ich erhielt, sahen fürchterlich aus – sie wurden bereits in einer Datenbank verwendet. Drei csv-Dateien (reine Textdateien) kamen. Ein Öffnen im Editor ergab, dass die Informationen durch senkrechte Striche (Pipes) voneinander getrennt sind.
([FHO
141
Sandini Bib
Abbildung 4.1: Die csv-Datei
Beim Öffnen der ersten Datei in Excel startet der Import-Assistent und fragt, wie die Spalten getrennt sind. Da die Felder nicht in einer festen Feldbreite vorliegen, sondern durch Trennzeichen getrennt sind, kann diese Option ausgewählt werden.
Abbildung 4.2: Der erste Schritt des Textkonvertierungs-Assistenten
142
'DWHQ LQ ([FHO XQG $FFHVV DXIEHUHLWHQ
Sandini Bib
Im zweiten Schritt wird dieses Trennzeichen Pipe ((Alt_Gr)+(>)) als Separator eingegeben. In der Vorschau werden nun die Spalten eindeutig getrennt dargestellt. Im dritten Schritt könnten „problematische“ Zeichen eliminiert werden. Dies ist jedoch nach einer Durchsicht nicht erforderlich.
Abbildung 4.3: Der zweite und dritte Schritt des Assistenten
([FHO
143
Sandini Bib
Das Ergebnis sieht auf den ersten Blick ganz passabel aus. Danach erfolgt eine genaue Kontrolle der Daten. Überflüssige Spalten können gelöscht werden (BEARBEITEN / ZELLEN LÖSCHEN).
4.1.2 Groß- und Kleinschreibung Eine zweite Durchsicht ergibt, dass die Texte der Tabelle in Großbuchstaben stehen. Hierzu wird eine neue Spalte angelegt (Menü EINFÜGEN), wo mit einer Funktion auf die andere Spalte verwiesen wird. Die Versalien können mit der Funktion =GROSS2(F2)
in Groß- und Kleinbuchstaben umgewandelt werden. Das Ergebnis wird am Kästchen nach unten gezogen – oder besser: mit einem Doppelklick nach unten vervollständigt. Dazu setzen Sie den Mauszeiger auf das kleine, schwarze Kästchen am unteren rechten Rand der Zelle und machen einen Doppelklick darauf. Die Formel wird danach so weit nach unten gezogen, wie die Spalte links daneben gefüllt ist.
Abbildung 4.4: Die Titel in Groß- und Kleinschreibung
Um nun die alte Spalte (hier: E) löschen zu können, wird das Ergebnis der „Berechnung“ markiert, kopiert und über die Funktion BEARBEITEN/ INHALTE EINFÜGEN „Werte“ als Inhalt über sich selbst geschrieben. Damit verschwindet die Funktion und wird durch die Werte ersetzt. Nun kann die Quellspalte problemlos gelöscht werden. Zugegeben: Das Ergebnis muss dennoch durchgesehen werden, denn ein Buchtitel wie „Diese Lang Vergessene Sehnsucht“ muss „per Hand“ in die korrekte Schreib-
144
'DWHQ LQ ([FHO XQG $FFHVV DXIEHUHLWHQ
Sandini Bib
weise gedreht werden. Dennoch: Ein großer Teil der Arbeit wurde durch diese Funktion abgenommen.
4.1.3 Informationen trennen In Spalte L befinden sich zwei Informationen in einer Spalte. Sie werden getrennt, indem nach der Spalte L eine neue Spalte eingefügt wird. Dann wird Spalte L markiert und mit Hilfe des Assistenten „Text in Spalten“ (im Menü DATEN) getrennt. Das Trennzeichen ist hier das Komma. Dummerweise werden in einigen Spalten Kommazahlen (15,5) getrennt. Dies kann mit dem Verkettungsoperator „&“ in der nächsten Spalte wieder zusammengefasst werden. Wenn Sie nicht wissen, wie viele Spalten das Ergebnis des Assistenten „Text in Spalten“ liefern wird, dann schieben Sie die zu trennende Spalte nach rechts und splitten sie dort in ihre Einzelbestandteile auf.
Abbildung 4.5: Der Textkonvertierungs-Assistent
Oder Sie verwenden die beiden Funktionen =LINKS(W2;FINDEN(",";W2)-1)
für den Text bis zum Komma und =RECHTS(W2;LÄNGE(W2)-FINDEN(",";W2))
([FHO
145
Sandini Bib
für den Text ab dem Komma. Soll der Text nach der Zeichenkette „cm,“ herausgelöst werden, so könnte dies mit =RECHTS(Y2;LÄNGE(Y2)-FINDEN("cm,";Y2)-3)
geschehen. Allerdings ist eine Fehlermeldung bei Texten ohne „cm“ die Folge. Dieser kann mit Hilfe der Funktion ISTFEHLER abgefangen werden: =WENN(ISTFEHLER(RECHTS(Y2;LÄNGE(Y2)FINDEN("cm,";Y2)-3));"";RECHTS(Y2;LÄNGE(Y2)-FINDEN("cm,";Y2)-3))
In Spalte P steht dummerweise der Preis als „24.95 DM“ und nicht als „24,95 DM“. Dies kann korrigiert werden, indem mit der Funktion WECHSELN der Punkt durch ein Komma ersetzt wird. Das Gleiche würde auch die Funktion ERSETZEN aus dem Menü BEARBEITEN leisten. Die Funktion hat den Vorteil, dass man in einem Schritt auch das „DM“-Zeichen entfernen könnte: =WERT(LINKS(WECHSELN(U2;".";",");SUCHEN(" ";U2)-1))
4.1.4 Informationen durch andere ersetzen Die Funktion WERT ist dabei nötig, denn das Ergebnis der Funktion LINKS ist eine Zeichenkette. Sie muss in eine Zahl transformiert werden, damit sie korrekt als Währung formatiert werden kann. Ein weiteres Problem stellt die Spalte „Key“ dar. Dort muss aus der Information „38#0009“ die Zahl 38 herausgeholt werden. Allerdings wird in einer anderen Tabelle („Rubriken.csv“) diese erläutert. Damit die jeweilige Nummer der entsprechenden Rubrik zugeordnet werden kann, muss diese importiert werden. Dazu wird ein neues Tabellenblatt eingefügt (EINFÜGEN/TABELLENBLATT). Auf dieses werden die Daten der Textdatei kopiert: DATEN/ TEXTDATEI IMPORTIEREN. Der Assistent durchläuft die bekannten Schritte. Wenn Sie zuvor im Assistenten „Text in Spalten“ das Leerzeichen eingeschaltet haben, so ist es noch immer aktiviert. Es muss natürlich ausgeschaltet werden. Stattdessen lautet das Trennzeichen wieder Pipe: „|“. Die Aufgabe lautet nun: Die Nummer aus Spalte C auf Tabellenblatt „Produkte“ soll ersetzt werden durch den zugehörigen Text der Nummer auf Tabellenblatt „Rubriken“. Dies leistet die Funktion SVERWEIS. Mit ihrer Hilfe kann festgelegt werden: =SVERWEIS(C2;Rubriken!$A$1:$J$97;4;FALSCH)
146
'DWHQ LQ ([FHO XQG $FFHVV DXIEHUHLWHQ
Sandini Bib
Dabei steht in der Zelle C2 der gesuchte Wert (38). Er wird auf dem Blatt „Rubriken“ gefunden – und zwar im Bereich A1:J97 – oder genauer: in der ersten Spalte dieses Bereichs. Wenn Sie mit dem Funktionsassistenten arbeiten, dann kann es sein, dass er statt des Bereichs den Namen der importierten Datei verwendet. Auch damit kann gearbeitet werden. Wenn Sie dagegen einen Zellenbezug verwenden, so achten Sie auf den absoluten Bezug, da dieser für alle Zeilen der Quellspalte derselbe sein muss. Mit der Zahl „4“ wird angegeben, in welcher Spalte der Matrix sich die Daten befinden, welche die Funktion anzeigen soll. Wird beim letzten Parameter „Bereich_Verweis“ nichts oder „WAHR“ angegeben, so könnte man mit dieser Funktion SVERWEIS Zwischenwerte ermitteln (was hier uninteressant ist). Die Rubrikendaten müssen dagegen in der korrekten Reihenfolge in der Liste stehen. Wird explizit der boolesche Wert „FALSCH“ verwendet, so müssen die Daten nicht sortiert vorliegen – es wird der exakte Wert gesucht. Soll die fünfte Spalte verwendet werden, so ist statt „4“ „5“ zu schreiben. Will man dagegen die Funktion so clever eingeben, dass man sie nicht nur nach unten, sondern auch nach rechts ziehen kann, so muss statt dem Wert „4“ eine Funktion stehen, die sich beim Ziehen nach rechts um 1 erhöht. Dies leistet die Funktion =SPALTE()
Wird sie in der Spalte D verwendet, so ergibt sie zufälligerweise den Wert 4. Würde man sie in E verwenden, so müsste man 1 abziehen – in C verwendet würde man 1 dazu zählen. Sollten in den Originaldaten einige „fehlerhafte“ Nummern stehen, denen keine Werte zugeordnet sind, so können diese wiederum mit Hilfe der Funktion ISTFEHLER vermieden werden. Die vollständige Funktion lautet also: =WENN(ISTFEHLER(SVERWEIS(C2;Rubriken!$A$1:$J$97;SPALTE(); FALSCH));"";SVERWEIS(C2;Rubriken!$A$1:$J$97;SPALTE();FALSCH))
Das Ergebnis der Funktion kann nun wieder in ihren Inhalt konvertiert werden. Und so sieht dann die komplette Datei aus:
([FHO
147
Sandini Bib
Abbildung 4.6: Die „geputzten“ Daten
4.1.5 Zusammenfassung Wenn Sie Daten als Export aus einem fremden Format erhalten, dann stehen sie häufig nicht in der gewünschten Form in der Excel-Tabelle. Dies kann mit Hilfe der Assistenten, die Excel bereitstellt, aber auch mit Hilfe der Funktionen von Excel bereinigt werden. Durch das schnelle Herunterkopieren von Funktionen und Ersetzen von Funktionen durch ihre Werte wird die Tabelle innerhalb von Minuten in die gewünschte Form gebracht. Dies ist bei einer Datenmenge von mehreren Hundert bis mehreren Tausend Datensätzen interessant. Bei weniger Daten können sie noch einmal eingegeben werden (dies ist wahrscheinlich schneller), bei mehreren Zehntausend Datensätzen wird Excel sehr langsam – die Obergrenze liegt bei 65.536. Bei solchen Mengen sollten Sie sich nach einem anderen Programm umsehen. Größere Datenmengen können mit Access bewältigt werden.
148
'DWHQ LQ ([FHO XQG $FFHVV DXIEHUHLWHQ
Sandini Bib
4.2
Access
4.2.1 Die Daten kommen Ein ähnliches Problem wie das oben beschriebene kann auch in Access vorkommen. In der Theorie (so jedenfalls alle Datenbank-Bücher) skizziert man mit Bleistift und Papier vor dem Anlegen seine Datenbank. Dann erst wird der Computer eingeschaltet. Die Praxis sieht aber meist anders aus. Gegeben sei ein Kunde, der seine Mitglieder bislang in einer Excel-Tabelle verwaltet hat. In eine bestehende Datenbank werden externe Daten über den Befehl DATEI/EXTERNE DATEN/IMPORTIEREN hereingeholt. Der Datentyp wird geändert, die Exceltabelle oder Textdatei ausgewählt. Liegen mehrere Tabellenblätter in der Excelmappe vor, wird nach dem Tabellenblatt gefragt. Bei einer Textdatei erfolgt die Frage nach den Trennzeichen. Die nächste Frage bezieht sich darauf, ob die Tabelle eine Spaltenüberschrift besitzt. Gemeint ist, ob in der ersten Zeile der Tabelle die Feldnamen stehen. Danach werden Sie gefragt, ob Sie die Daten in einer neuen Tabelle speichern möchten. Welch überflüssige Frage, da es doch keine andere Tabelle gibt, in die man die Daten hineinschreiben könnte. Schließlich kann man Aussagen über einzelne Felder treffen und den Typ ändern. Die Frage nach dem Primärschlüssel wird gestellt. Sie kann bejaht werden – es ist nie falsch, wenn eine Tabelle noch einen zusätzlichen Primärschlüssel hat. Und schließlich wird nach dem Namen der Tabelle gefragt. Sie soll bei uns „tbl_Mitglieder“ heißen. Wenn der Import ohne Fehler abgeschlossen ist, wird die Tabelle angezeigt, sonst wird gemeldet, dass eine Reihe von Feldern oder Datensätzen nicht erfolgreich importiert werden konnten. Das Öffnen der Tabelle sieht Erfolg versprechend aus, ein Blick „hinter die Kulissen“, das heißt in die Entwurfsansicht, jedoch weniger. Ärgerlich hierbei ist, dass Access sehr große Feldformate wählt. Diese müssen verkleinert werden. Der Feldname ID ist ein Primärschlüssel: Das ist gut so. Im Feld GESCHLECHT ist aufgeschlüsselt, dass „10“ Frau bedeutet und „20“ Männer. Dies muss nicht vom Typ „Double“ gespeichert werden – es genügt „Byte“. Der „Titel“ besitzt eine Feldgröße von 255 Zeichen: Das ist eindeutig zu viel. 20 Zeichen genügen. Auch „Name“, „Straße“ und „Ort“ können auf 100 Zeichen verkleinert werden. Die Postleitzahl sollte vom Felddatentyp „Zahl“ auf „Text“ verändert werden, damit den Städten mit führender „0“ in der Postleitzahl
..088
149
Sandini Bib
diese nicht abgeschnitten wird. Möglicherweise erhalten Sie beim Verkleinern der Werte eine Warnmeldung. Sollte wirklich etwas schief laufen, dann müssen Sie auf die Originaldaten zurückgreifen, die Sie doch hoffentlich gesichert haben.
Abbildung 4.7: Die Datenblatt- und die Entwurfsansicht der Tabelle
150
'DWHQ LQ ([FHO XQG $FFHVV DXIEHUHLWHQ
Sandini Bib
Abbildung 4.8: Die Warnmeldung beim Verkleinern der Feldgröße
4.2.2 Informationen trennen Die Tabelle kann erneut geöffnet werden. Es fallen einige unschöne Dinge auf: Vorname und Zuname stehen in einer Spalte. Das bedeutet, dass man nicht Herrn Müller oder Frau Maier anschreiben kann, sondern nur Frau Erna Maier und Herrn Hugo Müller. Das ist schlecht. Man kann die Tabelle auch nicht nach dem Zunamen sortieren. Deshalb muss sie getrennt werden. Um so etwas zu erledigen, wird eine neue Abfrage, basierend auf der Tabelle, erzeugt. Man kann nicht das „*“ herunterziehen, da sonst auf die ganze Tabelle zugegriffen wird. Besser ist es, jede einzelne Spalte herunterzuziehen oder doppelt anzuklicken oder alle Spalten zu markieren und herunterzuziehen.
Abbildung 4.9: Die Entwurfsansicht der Abfrage
Die Datenblattansicht würde keine Änderung zur ursprünglichen Tabelle ergeben. Nun kann die Spalte „Name“ gelöscht werden und dafür zwei weitere Spalten eingefügt werden (über die rechte Maustaste oder mit der Taste (Einfg)). In ihnen sollen der Vorname und der Zuname stehen. Dazu werden Funktionen benötigt. Access stellt über das Auf-
..088
151
Sandini Bib
bauen-Symbol den Ausdrucksgenerator zur Verfügung, der die Liste aller Funktionen anbietet:
Abbildung 4.10: Der Ausdrucksgenerator
Benötigt werden Textfunktionen. Die erste Textfunktion muss alle Zeichen des Feldes bis zum Leerzeichen herauslösen. Dies kann die Funktion Vorname: Links([Name];InStr(1;[Name];" ")-1)
Der Zuname wird abgetrennt über: Zuname: Rechts([Name];Länge([Name])-InStr(1;[Name];" "))
Sollte Ihnen die Eingabezeile zu klein sein, so kann die Zoom-Funktion über die Taste (ª)+(F2) aktiviert werden:
Abbildung 4.11: Die Zoom-Funktion vergrößert die Eingabezeile auf ein lesbares Maß
152
'DWHQ LQ ([FHO XQG $FFHVV DXIEHUHLWHQ
Sandini Bib
Die Datenblattansicht zeigt, dass nun Vor- und Zuname getrennt sind:
Abbildung 4.12: Vorname und Zuname stehen nun nebeneinander
Allerdings sollen diese Daten die Originaltabelle ersetzen. Also wird die Abfrage in der Entwurfsansicht als Tabellenerstellungsabfrage unter dem Namen „tbl_Mitglieder1“ (noch nicht die letzte ...) gespeichert. Die Abfrage wird mit einem Doppelklick zu einer Tabelle.
4.2.3 Daten normalisieren Unter Normalisieren versteht man das Aufteilen von Daten auf verschiedene Tabellen. Dabei stehen diese Daten in eindeutiger Beziehung zueinander. Und dafür gibt es gute Gründe: Man versucht Redundanzen, das heißt mehrfaches Speichern von Daten, zu vermeiden. Stellen Sie sich vor, bei einer Lieferung an verschiedene Kunden müssten in der Datenbank jedes Mal der gleiche Kundenname, seine Anschrift und der Ansprechpartner eingegeben werden. Das wäre sehr zeitaufwändig, fehlerträchtig (ein zweites Argument für die Normalisierung) und nicht sehr änderungsfreundlich (würde sich beispielsweise eine Adresse ändern, müsste man diese Adresse an mehreren Stellen ändern, um die Rechnungen zu stellen. Es geht also auch um die Wartung.
..088
153
Sandini Bib
Doch nun zum zweiten und schwierigeren Teil. In der Tabelle stehen die Bankverbindungen und Bankleitzahlen. Diese sollten in einer anderen Tabelle stehen und über einen Schlüssel verknüpft werden. Dazu müssen alle Bankverbindungen eindeutig herausgefiltert werden. Auch dies geschieht in einer Abfrage. Benötigt werden aus der Tabelle die Spalten „Bankleitzahl“ und „Bankverbindung“. Damit sie eindeutig angezeigt werden, wird über das Menü ANSICHT die Funktionen eingeschaltet – beide Spalten werden gruppiert.
Abbildung 4.13: Die Bankleitzahlen und die Bankbezeichnungen werden gruppiert.
Das Ergebnis ist eine zweispaltige Tabelle, in der alle Banknamen eindeutig auftauchen. Aus dieser Abfrage wird erneut eine Tabellenerstellungsabfrage gemacht und die Tabelle („tbl_Bankenliste“) wird erzeugt. Der erste Datensatz dieser Tabelle ist leer. Er wurde von all den Mitgliedern erzeugt, die keine Bankverbindung angegeben haben, da sie eine Rechnung geschickt bekommen möchten. Dort wird die Bankleitzahl „0“ eingetragen, die Bankbezeichnung lautet: „Ohne Bankverbindung“. Nun muss in der Mitgliedsliste überall dort, wo ein leeres Feld in der Bankverbindung steht, eine 0 stehen. Auch dies kann durch eine Abfrage generiert werden. BankleitzahlNeu: Wenn([Bankleitzahl] Ist Null;0;[Bankleitzahl])
So werden alle leeren Felder mit einer „0“ gefüllt. Auch aus dieser Abfrage wird eine neue Tabelle generiert.
154
'DWHQ LQ ([FHO XQG $FFHVV DXIEHUHLWHQ
Sandini Bib
Abbildung 4.14: Die leeren Einträge sind durch Nullen ersetzt worden
Die Tabelle der Bankenliste hat inzwischen einen Autowert erhalten, womit nun jeder Bank eine Nummer zugewiesen wurde. In einer weiteren Abfrage wird diese Tabelle („tbl_Bankenliste“) mit „tbl_Mitglieder2“ verknüpft, wobei die Bankleitzahlen miteinander verknüpft werden. Die Informationen über die Mitglieder werden aus „tbl_Mitglieder2“ herausgeholt, die Banknummer aus „tbl_Bankenliste“. Auch aus dieser Abfrage wird eine Tabelle erstellt: „t_Mitglieder3“. Ihr muss noch ein Feld eingefügt werden, in der sich ein Autowert befindet und in der ein Primärschlüssel gesetzt wurde. Damit sind die beiden Tabellen entwickelt, die nun im Menü EXTRAS/BEZIEHUNGEN in Beziehung zueinander gesetzt werden. Damit nicht eine Bank gelöscht werden kann, bei der es noch Mitglieder gibt, die ihre Gebühr über diese Bank abrechnen, wird die referentielle Integrität eingeschaltet:
Abbildung 4.15: Eine Beziehung zwischen den beiden Tabellen
..088
155
Sandini Bib
Damit neue Mitglieder nicht in die Tabelle eingetragen werden müssen, wird ein Eingabeformular erstellt:
Abbildung 4.16: Das Eingabeformular für die Mitglieder
Mit Hilfe des Assistenten wird ein Dropdown-Feld erzeugt, in dem der Benutzer die Bank auswählen kann. Auch das Geschlecht wird nicht direkt eingegeben, sondern über zwei Optionsbuttons geregelt, die die Nummern der Geschlechter („10“ für Frau, „20“ für Mann) als Index tragen. Werden nun diejenigen, die eine Rechnung erhalten, angeschrieben, dann können in einer Abfrage alle Mitglieder ohne Kontonummer oder mit Bankleitzahl = 0 herausgefiltert werden. Die Abfrage kann nach Namen sortiert werden.
4.2.4 Zusammenfassung Auch wenn es auf den ersten Blick paradox erscheint, Daten, die in einer Tabelle vorliegen, auf zwei oder mehrere Tabellen zu verteilen, um sie hinterher wieder per Abfrage zusammenzuführen, so hat diese Methode dennoch ihre Berechtigung. Bei großen Datenmengen verringert sich der Speicherbedarf, Änderungen können sehr schnell an einer Stelle
156
'DWHQ LQ ([FHO XQG $FFHVV DXIEHUHLWHQ
Sandini Bib
durchgeführt werden und die Eingabe wird erleichtert und führt nicht zu einer Vielzahl von Schreibvarianten. Für die Eingabe stellt Access Formulare zur Verfügung; gerade über Kombinations- oder Listenfelder können leicht Daten aus einer Tabelle herausgeholt und einer anderen zur Verfügung gestellt werden. Die Ausgabe erfolgt auf einem Bericht, der ausgedruckt werden kann, oder per Export in eine Textdatei oder Excelmappe. Oder in eine Textdatei im XML-Format.
4.3
Fragen zu Kapitel 4
4.3.1 Zu den Excel-Funktionen In der Spalte A stehen folgende Daten:
Abbildung 4.17: In der Spalte A stehen Daten
)UDJHQ ]X .DSLWHO
157
Sandini Bib
Übung 1 Mit welcher Funktion wird überprüft, ob in A der Text „Zwischensumme :“ steht? Schreiben Sie per Funktion ein „x“ in die Zelle daneben! Übung 2 Mit welcher Funktion kann man sich nur die Besoldungsgruppen anzeigen lassen, ohne dass der Wert „Zwischensumme“ in der Liste auftaucht? Übung 3 Mit welcher Funktion kann die Anzahl der Zeichen ermittelt werden? Übung 4 Mit welcher Funktion kann man sich in Spalte B nur die Zahlen anzeigen lassen, das heißt „A“ „löschen“? Übung 5 Wie kann man das Ergebnis in eine Zahl umwandeln? Übung 6 Wie könnte man diese Zahl mit 10 multiplizieren? Übung 7 Wie könnte man vor dieses Ergebnis den Buchstaben „C“ setzen? Übung 8 Wie könnte man die erste Spalte A löschen (ohne dass die berechneten Ergebnisse verloren gehen)? Übung 9 Kann man das Ergebnis auch mit einer einzigen Formel erhalten? Übung 10 Hätte man den Text „A“ auch mit Hilfe eines Assistenten löschen können?
158
'DWHQ LQ ([FHO XQG $FFHVV DXIEHUHLWHQ
Sandini Bib
4.3.2 Zu Access In einer Datenbank befinden sich in einer Tabelle drei Spalten.
Abbildung 4.18: Eine Tabelle enthält drei Spalten
Übung 1 Von welchem Felddatentyp sind sie deklariert? Übung 2 Wo und wie kann die Anzahl der unschädlichen Zusatzstoffe ermittelt werden? Übung 3 Wie kann aus der Spalte „Zusätze“ das „E“ herausgelöst werden? Übung 4 Wie kann man die daraus resultierende Zahl durch 10 teilen?
)UDJHQ ]X .DSLWHO
159
Sandini Bib
Übung 5 Wie kann man das Ergebnis auf null Nachkommastellen abrunden? Übung 6 Wie kann man den Buchstaben „C“ mit einem Leerzeichen vor das Ergebnis einfügen? Übung 7 Wie kann man nur die Texte anzeigen, deren Wirkung „unschädlich“ ist? Übung 8 Kann man dies in einer einzigen Formel darstellen? Übung 9 Wie kann man dieses Ergebnis in eine neue Tabelle schreiben?
160
'DWHQ LQ ([FHO XQG $FFHVV DXIEHUHLWHQ
Sandini Bib
5
le
n e rn
Zugriff auf die Datei mit VBA
Nun liegen eine Excel-Tabelle und eine Access-Datenbank vor uns, die darauf warten, als XML gespeichert zu werden. Dazu gibt es nun mehrere Wege. Bevor wir sie jedoch exportieren, einige wiederholende Worte zum VBA-Zugriff auf Excel und Access. Wem dies bekannt ist, der kann gerne das vorliegende Kapitel überspringen.
5.1
Excel
5.1.1 Das Objekt Application Oberstes Objekt von Excel ist Application. Dieses Objekt hat eine Reihe von Eigenschaften und Methoden: Application.Name
liefert den Name des Programms, also „Microsoft Excel“. Application.Path
liefert den Pfad der Programmdatei „Excel.exe“. Application.Caption
gibt den Text der Titelzeile zurück. Dieser kann, da es sich um eine Eigenschaft handelt, geändert werden, beispielsweise in Application.Caption = "Lotus 1-2-3"
5.1.2 Zugriff auf Excel-Arbeitsmappen Eine Excel-Datei, das heißt eine Arbeitsmappe, ist unterteilt in eines oder mehrere Arbeitsblätter. Diese wiederum sind untergliedert in Zellen, die in Zeilen und Spalten angeordnet sind.
([FHO
161
Sandini Bib
Die aktuelle Excel-Datei ist Application.ActiveWorkbook
Die Anzahl der (offenen) Excel-Dateien beträgt Application.Workbooks.Count
Man kann einen Zähler alle Dateien durchlaufen lassen oder direkt alle Objekte ansprechen: Sub Alle_Dateien1() Dim i As Integer Dim strDatName As String For i = 1 To Workbooks.Count strDatName = strDatName & vbCr & Workbooks(i).Name Next MsgBox strDatName End Sub
oder auch über einen direkten Objektzugriff: Sub Alle_Dateien2() Dim xlsDatei As Workbook Dim strDatName As String For Each xlsDatei In Workbooks strDatName = strDatName & vbCr & xlsDatei.Name Next MsgBox strDatName End Sub
Abbildung 5.1: Alle offenen Dateien
Somit kann überprüft werden, ob eine Datei schon geöffnet ist oder nicht. Falls ja, so wird sie nach vorne geholt, falls nein, dann wird sie geöffnet:
162
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib Sub DateiÖffnen() Dim xlsDatei As Workbook Dim strDatName As String On Error Resume Next For Each xlsDatei In Workbooks If xlsDatei.Name = "Zins und Tilgung.xls" Then xlsDatei.Activate Exit Sub End If Next Workbooks.Open Filename:= _ "C:\Eigene Dateien\Uebungsdateien\Excel\Zinsen.xls" End Sub
Eine Datei wird, wie oben ersichtlich, mit der Methode Open geöffnet, mit Close geschlossen und mit Save oder SaveAs gespeichert. Die Eigenschaft Saved prüft, ob eine Arbeitsmappe seit der letzten Änderung gespeichert wurde. Falls ja, wird der Wert „True“ zurückgegeben.
5.1.3 Zugriff auf Tabellenblätter Jede Excel-Datei hat eine oder mehrere Tabellenblätter. Auch diese können durchlaufen werden. Dabei ist das Objekt von Workbook entweder Sheet oder WorkSheet. Sheet ist dabei allgemeiner, da Tabellenblätter auch Diagramme beinhalten können. Deklariert wird es allerdings vom Objekttyp Worksheet. Sub TabellenBlätterDurchlaufen() Dim xlsTabBlatt As Worksheet Dim strBlattName As String On Error Resume Next For Each xlsTabBlatt In Sheets strBlattName = strBlattName & vbCr & xlsTabBlatt.Name Next MsgBox strBlattName End Sub
([FHO
163
Sandini Bib
Abbildung 5.2: Alle Tabellennamen werden angezeigt
Ein Blatt wird mit der Methode Activate aktiviert. Ist es verborgen, so kann dies mit der Eigenschaft Visible überprüft werden. Gelöscht wird ein Blatt mit der Methode Delete, hinzugefügt mit Add. Die Eigenschaft Name übergibt den Namen. Im folgenden Programm wird der Benutzer nach dem Namen eines Tabellenblatts gefragt. Existiert es, so wird es angesprungen, existiert es nicht, so erhält der Benutzer einen Hinweis: Sub Tabellenblättersuche() Dim xlsTabBlatt As Worksheet Dim strBlattName As String On Error Resume Next strBlattName = InputBox("Wie lautet der Name des " & _ "gesuchten Blatts?", "Blattsuche") For Each xlsTabBlatt In Sheets If LCase(strBlattName) = LCase(xlsTabBlatt.Name) Then xlsTabBlatt.Activate Exit Sub End If Next MsgBox "Das gesuchte Blatt " & strBlattName & _ " wurde leider nicht gefunden." End Sub
Will man auch Textteile suchen können, dann muss die If-Verzweigung modifiziert werden: If InStr(LCase(xlsTabBlatt.Name), _ LCase(strBlattName)) > 0 Then [...]
164
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib
5.1.4 Zugriff auf Zellen Die wohl häufigste Zugriffsart ist sicherlich der Zellzugriff. Dabei kann der Cursor auf eine Zelle gesetzt und diese (ActiveCell) dann modifiziert werden. Eleganter ist dagegen ein indirekter Verweis mit Hilfe von Objektvariablen. Hierzu steht eine ganze Reihe von Möglichkeiten zur Verfügung. Angenommen, Sie möchten auf die Zelle B41 des Tabellenblatts „Hitchcock“ der Datei „Filme.xls“ zugreifen. Ist diese Datei offen, dann kann sie angesprungen werden. Danach wird das Tabellenblatt aktiviert und schließlich die Zelle. Der Zugriff darüber erfolgt mit dem Objekt Range oder dem Objekt Cells: Sub AufB41Zugreifen() Application.Workbooks("Filme.xls").Activate ActiveWorkbook.Sheets("Hitchcock").Activate ActiveSheet.Range("B41").Activate End Sub
Nun sitzt der Cursor auf der Zelle B41. Den Inhalt dieser Zelle könnte man mit MsgBox ActiveCell.Value
auslesen. Eleganter ist es dagegen mit einem direkten Objektzugriff: MsgBox Application.Workbooks("Filme.xls"). _ Sheets("Hitchcock").Range("B41").Value
Solch eine riesige Befehlszeile ist weder übersichtlich noch praktisch zum Fehlerabfangen. Deshalb empfiehlt sich das Aufsplitten und Aufteilen in Objektvariablen, beispielsweise so: Sub AufB41Zugreifen3() Dim xlsDatei As Workbook Dim xlsTabelle As Worksheet Dim xlsZelle As Range Set xlsDatei = Set xlsTabelle Set xlsZelle = ' Alternativ: Set xlsZelle =
Zellinhalte können abgefragt (wie oben) oder auch gesetzt werden. Der Befehl xlsZelle.Value = "Psycho II"
schreibt den Wert „Psycho II“ in die Zelle. Statt des Objekts Range kann auch Cells als Kollektion verwendet werden: Set xlsZelle = xlsTabelle.Cells(41, 2)
Dabei wird zuerst die Zeile (Rows) und dann die Spalte (Columns) angegeben. Der Zugriff über Cells eignet sich sehr gut, wenn mit Variablen gearbeitet wird. Mit dem Objekt Range kann nicht nur auf eine Zelle zugegriffen werden: ActiveSheet.Range("A4").Activate
sondern auch auf einen Bereich: ActiveSheet.Range("A4:D7").Activate
Ebenso wählt ActiveSheet.Range("A1:D7").Select
diesen Bereich aus. Mit Activate kann zusätzlich eine Zelle innerhalb des markierten Bereichs ausgewählt werden: ActiveSheet.Range("A1:F7").Select ActiveSheet.Range("C3").Activate
Auf den ersten Blick umständlicher funktioniert das Auswählen über den Bereich: ActiveSheet.Range(ActiveSheet.Cells(1, 1), _ ActiveSheet.Cells(3, 3)).Activate
Der Vorteil davon ist allerdings, dass die Eckkoordinaten getrennt berechnet werden können: x1 x2 y1 y2
Ähnlich wie die Range-Methode einen Bereich auswählt (oder bearbeitet), kann mit dem Objekt beziehungsweise der Methode Worksheets ein
166
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib
Arbeitsblatt aktiviert werden. Soll beispielsweise der Zellbereich „A1:C3“ des Blatts „Tabelle3“ fett formatiert werden, so kann dies folgendermaßen eingegeben werden: Worksheets("Tabelle3").Activate x1 = 1 x2 = 3 y1 = 1 y2 = 3 With ActiveSheet.Range(ActiveSheet.Cells(x1, y1), _ ActiveSheet.Cells(x2, y2)) .Select .Font.Bold = True End With
Mit einem ähnlichen Objekt wie Cells kann man sich bewegen. Sitzt der Cursor auf der Zelle B9, so wird er auf die Zelle B10 mit dem Befehl ActiveCell.Offset(1, 0).Activate
gesetzt. Von B9 wird er auf B8 mit ActiveCell.Offset(-1, 0).Activate
bewegt, auf C9 mit ActiveCell.Offset(0, 1).Activate
Soll dagegen nur ein Wert überprüft oder gesetzt werden, so genügt MsgBox ActiveCell.Offset(1, 0).Value
Der Cursor bleibt auf der alten Zelle sitzen und zeigt den Wert der darunter liegenden Zelle an. Wird nun ein bestimmter Bereich durchlaufen, so hilft hierbei das Objekt CurrentRegion. Es hat die beiden Eigenschaften Rows und Columns, die beide wiederum die Eigenschaft Count besitzen. Darüber kann die Anzahl der Zeilen oder Spalten aus einem ausgefüllten Bereich ermittelt werden. In einer Tabelle sind drei Spalten ausgefüllt. In der ersten, die mit Nummer überschrieben ist, stehen fortlaufende Nummern. In der zweiten Spalte stehen die Bezeichnungen, die sich hinter den Nummern verbergen, in der dritten die (fiktiven) Preise (für die Videos):
([FHO
167
Sandini Bib
Abbildung 5.3: In der linken Spalte stehen die fortlaufenden Nummern, in der mittleren die Artikel und in der rechten die Preise
Der Benutzer wird nach einer Nummer gefragt. Er trägt sie in eine Inputbox ein und erhält den Namen des Films: Sub FilmAnzeigen1() Dim intNr As Integer Dim intZähler As Integer On Error GoTo ende ActiveWorkbook.Sheets("Hitchcock").Activate ActiveSheet.Range("A1").Select intZähler = 0 intNr = InputBox("Bitte eine Nummer eingeben") With ActiveCell For intZähler = 1 To .CurrentRegion.Rows.Count If ActiveCell.Offset(intZähler, 0).Value = intNr Then MsgBox "Der Film mit der Nummer " & _ intNr & " lautet: " & vbCr & Chr(187) & _ ActiveCell.Offset(intZähler, 1).Value & _ Chr(171) & vbCr & " und kostet " & _ FormatCurrency(ActiveCell.Offset _ (intZähler, 2).Value)
168
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib Exit Sub End If Next intZähler End With MsgBox "Schade, aber die Nummer " & intNr & _ " wurde nicht gefunden!" Exit Sub ende: MsgBox "Es trat ein Fehler auf: " & _ Err.Description, vbCritical, "Fehler!" End Sub
Abbildung 5.4: Die eingegebene Nummer lautet 136, das Ergebnis „Immer Ärger mit Harry“ zu 39,90 DM
Die Vorgehensweise: Der Benutzer wird nach einer Nummer gefragt. Im Tabellenblatt „Hitchcock“ wird die Zelle A1 aktiviert. Eine For ... NextSchleife durchläuft die Tabelle von A1 bis zu der letzten gefüllten Zelle, die über die Eigenschaft ActiveCell.CurrentRegion.Rows.Count ermittelt wird. Jede der Zellen wird mit dem Inhalt der Input-Box-Variablen verglichen (intNr). Sind sie gleich, hüpft der Zeiger eine Spalte nach rechts und zeigt den Inhalt dieser Zelle an. Wird die Schleife ohne Erfolg durchlaufen, dann wird die Meldung unterhalb der Schleife angezeigt. Das Ganze funktioniert allerdings eleganter, ohne dass die Position des Markierungszeigers verändert wird: Sub FilmAnzeigen2() Dim xlApp As Application Dim xlMappe As Workbook Dim xlTabelle As Worksheet Dim xlZelle As Range Dim intNr As Integer Dim intZähler As Integer On Error GoTo ende Set xlApp = Application Set xlMappe = xlApp.ActiveWorkbook Set xlTabelle = xlMappe.Sheets("Hitchcock")
([FHO
169
Sandini Bib Set xlZelle = xlTabelle.Range("A1") intZähler = 0 intNr = InputBox("Bitte eine Nummer eingeben") With xlZelle For intZähler = 1 To .CurrentRegion.Rows.Count If ActiveCell.Offset(intZähler, 0).Value = intNr Then MsgBox "Der Film mit der Nummer " & _ intNr & " lautet: " & vbCr & Chr(187) & _ xlZelle.Offset(intZähler, 1).Value & _ Chr(171) & vbCr & " und kostet " & _ FormatCurrency(xlZelle.Offset(intZähler, _ 2).Value) Exit Sub End If Next intZähler End With MsgBox "Schade, aber die Nummer " & intNr & _ " wurde nicht gefunden!" Exit Sub ende: MsgBox "Es trat ein Fehler auf: " & _ Err.Description, vbCritical, "Fehler!" End Sub
Nun soll ein zweites Symbol in der Filmliste dafür sorgen, dass der Benutzer einen neuen Artikel eintragen kann. Ihm wird automatisch die nächsthöhere Nummer vergeben und der Artikel wird unten an die Liste angefügt. Die Liste könnte mit einer Do ... Loop-Until-Schleife durchlaufen werden (wie in „Filmanzeigen1“) oder indem die Anzahl der vorhandenen Zellen bestimmt wird. Letzteres ist eleganter: Sub NeuerFilm() Dim xlApp As Application Dim xlMappe As Workbook Dim xlTabelle As Worksheet Dim xlZelle As Range Dim intZeilen As Integer Dim strNeuTitel As String Dim curNeuPreis As Currency
intZeilen = xlZelle.Rows.Count strNeuTitel = InputBox("Wie lautet der Name des " & _ "neuen Films?") curNeuPreis = InputBox("Was kostet " & strNeuTitel & "?") With xlZelle .Offset(intZeilen, 0).Value = 100 + intZeilen .Offset(intZeilen, 1).Value = strNeuTitel .Offset(intZeilen, 2).Value = curNeuPreis End With End Sub
Abbildung 5.5: Die Texteingabe ...
Abbildung 5.6: ... und das Ergebnis – Text wird eingefügt und eine neue Nummer wird vergeben
([FHO
171
Sandini Bib
Damit sind die wichtigsten Excel-Befehle erklärt, mit denen man per VBA auf Daten innerhalb von Zellen zugreifen kann. Zurück zu unserer Liste. Dort sollen nun die Daten in eine Textliste, die zur Veröffentlichung dient, exportiert werden. Beispielsweise so: Sub TextStreamTest() Dim fsDatei As Object Dim fsInhalt As Object Dim xlZelle As Range Dim i As Integer Set fsDatei = CreateObject("Scripting.FileSystemObject") fsDatei.CreateTextFile _ "c:\Eigene Dateien\XML\Kapitel05\test.txt" ' -- Erstellen einer Datei Set fsDatei = fsDatei.GetFile( _ "c:\Eigene Dateien\XML\Kapitel05\test.txt") Set fsInhalt = fsDatei.OpenAsTextStream(2, -2) Set xlZelle = _ ActiveWorkbook.Worksheets("Almodóvar").Range("B1") i = 1 Do While Not xlZelle.Offset(i, 0).Value = "" fsInhalt.write xlZelle.Offset(i, 0).Value fsInhalt.write vbCr i = i + 1 Loop fsInhalt.Close End Sub
Mit Hilfe der FileSystemObject-Klasse kann der Text in eine reine Textdatei geschrieben werden. Nun soll allerdings jeder Artikelname mit einem Tag gekennzeichnet werden. Dazu wird zu Beginn in spitzen Klammern eingeschlossen „Artikelname“ gesetzt. Am Ende wird dieses Tag geschlossen. Also so: Alles über meine Mutter Sub TextStreamTestXML() Dim fsDatei As Object Dim fsInhalt As Object
172
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib Dim xlZelle As Range Dim i As Integer Set fsDatei = CreateObject("Scripting.FileSystemObject") fsDatei.CreateTextFile _ "c:\Eigene Dateien\XML\Kapitel05\test02.xml" ' -- Erstellen einer Datei Set fsDatei = fsDatei.GetFile( _ "c:\Eigene Dateien\XML\Kapitel05\test02.xml") Set fsInhalt = fsDatei.OpenAsTextStream(2, -2) Set xlZelle = ActiveWorkbook.Worksheets _ ("Almodóvar").Range("B1") i = 1 fsInhalt.write "" fsInhalt.write "" Do While Not xlZelle.Offset(i, 0).Value = "" fsInhalt.write "" fsInhalt.write xlZelle.Offset(i, 0).Value fsInhalt.write "" i = i + 1 Loop fsInhalt.write "" fsInhalt.Close End Sub
Dem aufmerksamen Betrachter des Codes wird sicherlich nicht entgangen sein, dass die neue Datei nun „test2.xml“ heißt und nicht mehr die Endung „txt“ trägt. Dies hat seinen guten Grund: denn nun haben wir ein gültiges XML-Dokument. Mit einem Doppelklick auf die Datei öffnet sich der Microsoft Internet-Explorer und zeigt die Liste an. Das war ja auch unser vorrangiges Ziel.
([FHO
173
Sandini Bib
Abbildung 5.7: Die Datei wird im Internet Explorer geöffnet
Hätten wir einen Fehler gemacht, also beispielsweise den Beginn des Artikels, der durch "" geöffnet wurde, nicht geschlossen, so hätten wir beim Öffnen im Explorer eine Fehlermeldung erhalten:
Abbildung 5.8: Ein fehlerhaftes XML-Dokument
Die Technik ist (noch) nicht überzeugend. Wie man eleganter und schneller mit VBA ein XML-Dokument erzeugen kann, wird im nächsten Kapitel beantwortet.
174
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib
5.2
Zugriff aus Access
Mit Access 2000 führt Microsoft ein weiteres Datenbankzugriffsobjekt ADO (ActiveX-Datenobjekt) ein, welches mehr und mehr DAO (Datenzugriffsobjekt) verdrängt. Da in VB 6.0 ADO eine größere Rolle spielt als DAO und zu erwarten ist, dass mit VB.NET eine erweiterte Version ADO.NET erscheinen wird, soll an dieser Stelle nur der Datenzugriff per ADO gezeigt werden. Beachten Sie vor der Programmierung, dass in Access der korrekte Verweis auf „Microsoft ADO“ gesetzt wurde und nicht auf DAO (Menü EXTRAS/VERWEISE).
5.2.1 Das Connection-Objekt Oberstes Objekt stellt das Connection-Objekt dar – es definiert eine Sitzung für einen Benutzer und eine Datenquelle. Es wird deklariert: Dim cnn As ADODB.Connection
Mit seiner Hilfe wird auf das aktuelle Projekt zugegriffen: cnn.Open CurrentProject.Connection
Oder ganz sauber: Sub ConnectionZugriff() Dim cnn As ADODB.Connection Set cnn = New ADODB.Connection cnn.Open CurrentProject.Connection cnn.Close Set cnn = Nothing End Sub
Natürlich kann man auch auf eine andere Sitzung zugreifen, beispielsweise so: Set cnn = New ADODB.Connection cnn.Open "Provider=sqloledb;" & _ "Data Source=srv;Initial Catalog=Pubs" & _ ";User Id=sa;Password=;"
5.2.2 Die Objekte der Datenbank Die Datenbank besteht aus einer Reihe von Elementen: Tabellen, Abfragen und Beziehungen. Hierzu wird das Recordset-Objekt eingesetzt. Die korrekte Deklaration lautet:
=XJULII DXV $FFHVV
175
Sandini Bib Dim rs As ADODB.Recordset
Mit Hilfe eines SQL-Strings kann nun auf eine Tabelle zugegriffen werden: Set rs = New ADODB.Recordset rs.Open "SELECT * FROM tab_Mitglieder", _ CurrentProject.Connection
Hat man vorher cnn korrekt geöffnet, kann man die letzte Zeile auch schreiben als: rs.Open "SELECT * FROM tab_Mitglieder", cnn
Nun könnte man die Daten anzeigen lassen und das Recordset-Objekt schließen: MsgBox rs.GetString rs.Close Set cnn = Nothing Set rs = Nothing
Der komplette Code lautet dann: Sub ConnectionZugriff2() Dim cnn As ADODB.Connection Dim rs As ADODB.Recordset Set cnn = New ADODB.Connection Set rs = New ADODB.Recordset cnn.Open CurrentProject.Connection rs.Open "SELECT * FROM tab_Mitglieder", cnn MsgBox rs.GetString rs.Close cnn.Close Set cnn = Nothing Set rs = Nothing End Sub
Abbildung 5.9: Einige der Daten
176
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib
Man kann allerdings auch mit dem Objekt Command direkt auf eine Abfrage, SQL-Anweisung oder gespeicherte Prozedur zugreifen. Die Deklaration lautet:
5.2.3 Die ADO-Datensatzgruppen Wird ein Recordset-Objekt mit der Methode Open geöffnet, dann verfügt es über drei Parameter, die sich auf die Art der erstellten Datensatzgruppe auswirken: CursorType, LockType und Options.
&XUVRU7\SH Wenn Sie eine Datensatzgruppe öffnen, dann wird CursorType als Standard auf adOpenForwardOnly gesetzt. Das heißt, dass nur ein Vorwärtsbewegen innerhalb der Datensätze möglich ist. Einige Eigenschaften und Methoden, wie beispielsweise RecordCount und MovePrevious, sind dadurch nicht verfügbar. Wird die Datensatzgruppe dagegen adOpenStatistic geöffnet, so kann man sich vorwärts und rückwärts durch die Daten bewegen. Änderungen an den Daten werden allerdings von den anderen Benutzern der Datenbank nicht wahrgenommen. Dies wird jedoch durch die Option adOpenKeyset ermöglicht. Die Option mit den meisten Möglichkeiten stellt adOpenDynamic dar. Sub DatenZugriff1() Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.Open "SELECT * FROM tab_Mitglieder", , adOpenStatic MsgBox rs.RecordCount rs.Close Set rs = Nothing End Sub
Die Anzahl der Datensätze kann weder mit adOpenDynamic noch mit adOpenForwardOnly ermittelt werden. Übrigens könnte das RecordsetObjekt auch folgendermaßen geöffnet werden: rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenStatic rs.Open "SELECT * FROM tab_Mitglieder"
Wichtig ist hierbei die Reihenfolge: Zuerst muss der CursorType festgelegt werden, dann kann das Objekt geöffnet werden.
=XJULII DXV $FFHVV
177
Sandini Bib
LockType Die Eigenschaft LockType gestattet oder verbietet Änderungen an der Datensatzgruppe. Sie verfügt über folgende Parameter: adLockReadOnly, adLockPessimistic, adLockOptimistic und adLockBatchOptimistic. Die voreingestellte Eigenschaft adLockReadOnly erlaubt nur das Lesen der Datensätze. Bei der Option adLockPessimistic wird der Datensatz gesperrt, sobald eine Änderung einsetzt. Bei adLockOptimistic dagegen erst, wenn er mit gespeichert wird – per Programmierung geschieht dies mit der Methode Update. Die Eigenschaft adLockBatchOptimistic kann eine Sperrung hinausschieben, was interessant ist, wenn mehrere Datensätze nacheinander bearbeitet werden. Sub DatenZugriff2() Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenStatic rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" MsgBox rs.Fields("tMitZuname").Value rs.Fields("tMitZuname").Value = "Hotzenplatz" rs.Update MsgBox rs.Fields("tMitZuname").Value rs.Close Set rs = Nothing End Sub
Options Der Parameter Options legt fest, wie der Datenanbieter das die Quelle bezeichnende Argument einrichten soll. Wird der Wert auf AdCmdText gesetzt, dann wird die Quelle als Befehl, das heißt als SQL-Anweisung verarbeitet.
5.2.4 Bewegen innerhalb von Datensätzen Wird die Tabelle oder Abfrage mit adOpenStatistic oder adOpenKeyset geöffnet, dann kann man sich mit Hilfe der folgenden Methoden durch die Datensätze bewegen:
178
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib Methode
Erklärung
MoveFirst
geht zum ersten Datensatz.
MoveLast
geht zum letzten Datensatz.
MoveNext
geht zum nächsten Datensatz.
MovePrevious
geht zum vorhergehenden Datensatz.
Move Zeilen, Start
geht die Anzahl der Zeilen nach unten (bei negativen Zahlen nach oben). Start ist optional und kennzeichnet ein Lesezeichen.
Tabelle 5.1: Methoden des Recordsets zum Bewegen über eine Tabelle
Im folgenden Beispiel ist die erste Methode MoveFirst überflüssig, da nach dem Öffnen der Datensatzzeiger immer auf den ersten Datensatz hinweist. Sub Bewegung1() Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenStatic rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" rs.MoveFirst MsgBox rs.Fields("tMitZuname").Value rs.MoveNext MsgBox rs.Fields("tMitZuname").Value rs.MoveLast MsgBox rs.Fields("tMitZuname").Value rs.MovePrevious MsgBox rs.Fields("tMitZuname").Value rs.Close Set rs = Nothing End Sub
=XJULII DXV $FFHVV
179
Sandini Bib
Abbildung 5.10: Der erste und der zweite Datensatz
Einige Eigenschaften des RecordSets: Eigenschaft
Beschreibung
RecordCount
Anzahl der Datensätze
BOF
Begin Of File. BOF ist wahr, wenn der Datensatzzeiger auf dem ersten Datensatz steht.
EOF
End Of File. EOF ist wahr, wenn der Datensatzzeiger auf dem letzten Datensatz steht
AbsolutePosition
die absolute Position – die Datensatznummer
EditMode
ist wahr, wenn ein Datensatz bearbeitet wird.
Tabelle 5.2: Eigenschaften von Recordset
Die Methoden und Eigenschaften können auf folgende gleichwertige Weisen geschrieben werden: MsgBox MsgBox MsgBox MsgBox MsgBox
Angezeigt wird der Inhalt des Felds „tMitZuname“ des aktuellen Datensatzes. Die eckigen Klammern sind unbedingt nötig, wenn der Feldname ein oder mehrere Leerzeichen enthält. Statt des Namens könnte auch die Nummer der Spalte verwendet werden. Wenn „tMitZuname“ die sechste Spalte ist, so wird der Inhalt auch mit: MsgBox rs.Fields(5).Value
angezeigt. Da Fields eine Aufzählung ist, können alle Daten des Datensatzes durchlaufen werden. Achtung: Die Zählung beginnt bei 0!
180
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib Sub ZeigeFeldnamen1() Dim rs As ADODB.Recordset Dim i As Integer Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenStatic rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" For i = 0 To rs.Fields.Count - 1 MsgBox rs.Fields(i).Name Next rs.Close Set rs = Nothing End Sub
Oder auch per Zugriff auf jedes Objekt: Sub ZeigeFeldnamen2() Dim rs As ADODB.Recordset Dim fd As ADODB.Field Dim i As Integer Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenStatic rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" For Each fd In rs.Fields MsgBox fd.Name Next rs.Close Set rs = Nothing End Sub
Mit der Eigenschaft „Value“ kann der Inhalt des ersten Datensatzes ausgelesen werden. Jedoch Achtung: Wenn ein Feld leer ist, dann kommt es zu einer Fehlermeldung. Man muss überprüfen, ob sich etwas im Feld befindet: If IsNull(fd.Value) = False Then MsgBox fd.Value End If
Und so sieht der vollständige Code aus:
=XJULII DXV $FFHVV
181
Sandini Bib Sub Bewegung2() Dim rs As ADODB.Recordset Dim strListe As String Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenStatic rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" Do If IsNull(rs.Fields("tMitZuname").Value) = False Then strListe = strListe & ", " & _ rs.Fields("tMitZuname").Value End If rs.MoveNext Loop Until rs.EOF MsgBox strListe rs.Close Set rs = Nothing End Sub
Die Schleife könnte auch mit dem Befehl Loop While Not rs.EOF
beendet werden.
Abbildung 5.11: Alle Zunamen-Felder
182
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib
Leider passen nicht alle Daten der Spalte „Zuname“ der Tabelle „tab_Mitglieder“ in ein Meldungsfenster.
5.2.5 Suchen von Datensätzen Neben dem Ändern von vorhandenen Daten und dem Hinzufügen von neuen ist sicherlich das Suchen (und Finden) von Daten die wichtigste Datenbankoperation. Eine (wenig elegante) Möglichkeit besteht darin, alle Datensätze zu durchlaufen und mit einem Eintrag zu vergleichen. Im folgenden Beispiel wird der Benutzer aufgefordert einen Namen einzugeben. Danach erhält er den Vornamen: Sub Suchen1() Dim rs As ADODB.Recordset Dim strName As String Dim strAnrede As String Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenStatic rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" strName = InputBox("Bitte einen Zunamen eingeben") Do If rs.Fields("tMitZuname").Value = strName Then If rs.Fields("tMitGeschlecht") = 1 Then strAnrede = "Frau " Else strAnrede = "Herr " End If If MsgBox(strAnrede & strName & vbCr & _ "heißt mit Vornamen: " & _ rs.Fields("tMitVorname").Value & vbCr & _ "Wollen Sie weitersuchen?", vbYesNo) = vbNo Then Exit Sub End If End If rs.MoveNext Loop Until rs.EOF MsgBox "Es wurde kein (weiterer) Datensatz mit " & _ strName & " gefunden." rs.Close Set rs = Nothing End Sub
=XJULII DXV $FFHVV
183
Sandini Bib
Eleganter und schneller wird mit einer SQL-Anweisung und der Methode Find gesucht: Sub Suchen2() Dim rs As ADODB.Recordset Dim strAnrede As String Dim dblNr As Double Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenStatic rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" dblNr = InputBox("Bitte eine Mitgliedsnummer eingeben") rs.Find "[tMitMitgliedsnummer] = " & dblNr If rs.EOF Then MsgBox "Es wurde kein Datensatz mit " & _ dblNr & " gefunden." Else If rs.Fields("tMitGeschlecht") = 1 Then strAnrede = "Frau " Else strAnrede = "Herr " End If MsgBox strAnrede & rs.Fields("tMitZuname").Value & _ vbCr & "hat die Mitgliedsnummer: " & _ vbCr & dblNr End If rs.Close Set rs = Nothing End Sub
Abbildung 5.12: Ein Datensatz wurde gefunden
Achtung: Bei Texten muss ein Anführungszeichen in das Suchkriterium eingefügt werden:
184
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib Sub Suchen3() Dim rs As ADODB.Recordset Dim strAnrede As String Dim strName As String Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenStatic rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" strName = InputBox("Bitte einen Zunamen eingeben.") rs.Find "[tMitZuname] = """ & strName & """" If rs.EOF Then MsgBox "Es wurde kein Datensatz mit " & strName & _ " gefunden." Else If rs.Fields("tMitGeschlecht") = 1 Then strAnrede = "Frau " Else strAnrede = "Herr " End If MsgBox strAnrede & strName & vbCr & _ "heißt mit Vornamen: " & _ rs.Fields("tMitVorname").Value End If rs.Close Set rs = Nothing End Sub
Soll das Sternchen oder Fragezeichen als Platzhalter eingegeben werden, dann muss mit dem Vergleichsoperator LIKE gearbeitet werden: rs.Find "[tMitZuname] LIKE """ & strName & """"
Man könnte nun in der Tabelle weitersuchen. Geschickter ist es hingegen, wenn die Tabelle zuerst gefiltert wird. Dann liegen alle Daten, welche das gesuchte Kriterium erfüllen, vor:
5.2.6 Filtern und Sortieren Sehr einfach lässt sich eine Datenbank mit dem Befehl Sort sortieren. Davor muss die Datenbank jedoch erneut geöffnet werden. Das Sortierkriterium kann dabei in eckigen Klammern stehen. Im folgenden Bei-
=XJULII DXV $FFHVV
185
Sandini Bib
spiel werden die Datensätze einmal unsortiert und einmal sortiert ausgegeben: Sub Sortieren() Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset Dim strListe As String rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenStatic rs.LockType = adLockOptimistic rs.CursorLocation = adUseClient rs.Open "SELECT * FROM tab_Mitglieder" Do If IsNull(rs.Fields("tMitZuname").Value) = False Then strListe = strListe & ", " & _ rs.Fields("tMitZuname").Value End If rs.MoveNext Loop While Not rs.EOF MsgBox strListe, vbInformation, "Unsortiert" strListe = "" rs.Sort = "tMitZuname ASC" Do If IsNull(rs.Fields("tMitZuname").Value) = False Then strListe = strListe & ", " & _ rs.Fields("tMitZuname").Value End If rs.MoveNext Loop While Not rs.EOF MsgBox strListe, vbInformation, "Sortiert" rs.Close Set rs = Nothing End Sub
Das Sortierkriterium könnte auch folgendermaßen geschrieben werden: rs.Sort = "[tMitZuname] ASC"
Wichtig ist hierbei die Eigenschaft CursorLocation des Recordsets. Sie muss auf adUseClient für den clientseitigen Zugriff auf das Recordset gesetzt werden.
186
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib
Ähnlich wie das Sortieren funktioniert das Filtern mit der Methode Filter. Dabei wird das Filterkriterium als String übergeben. Beispiele: "[Ort] Like 'D*'" "[Jahresbeitrag] > 179" "[Ort] Like 'D*' And [Jahresbeitrag] > 179"
Wenn Sie Schwierigkeiten mit der Syntax haben, so können Sie diese dem SQL-Fenster der Abfragen entnehmen. Jedoch Achtung: SQL verlangt am Ende ein Semikolon – dies darf beim Filterkriterium nicht stehen! Im folgenden Beispiel werden die drei Filterkriterien angewendet und das Ergebnis angezeigt: Sub Filter() Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset Dim strListe As String Dim strSQL As String rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenKeyset rs.Open "SELECT * FROM tab_Mitglieder" Do If IsNull(rs.Fields("tMitZuname").Value) = False Then strListe = strListe & ", " & _ rs.Fields("tMitZuname").Value End If rs.MoveNext Loop Until rs.EOF MsgBox strListe, vbInformation, "Ungefiltert" strListe = "" strSQL = "tMitZuname LIKE 'M*'" rs.Filter = strSQL If rs.RecordCount > 0 Then rs.MoveFirst Do If IsNull(rs.Fields("tMitZuname").Value) = False Then strListe = strListe & ", " & _ rs.Fields("tMitZuname").Value End If rs.MoveNext Loop Until rs.EOF MsgBox strListe, vbInformation, "Filter I" End If
=XJULII DXV $FFHVV
187
Sandini Bib strListe = "" strSQL = "[tMitJahresbeitrag] > 179" rs.Filter = strSQL If rs.RecordCount > 0 Then rs.MoveFirst Do If IsNull(rs.Fields("tMitZuname").Value) = False Then strListe = strListe & ", " & _ rs.Fields("tMitZuname").Value End If rs.MoveNext Loop Until rs.EOF MsgBox strListe, vbInformation, "Filter II" End If strListe = "" strSQL = "[tMitJahresbeitrag] > 179 AND [tMitZuname] LIKE 'M*'" rs.Filter = strSQL If rs.RecordCount > 0 Then rs.MoveFirst Do If IsNull(rs.Fields("tMitZuname").Value) = False Then strListe = strListe & ", " & _ rs.Fields("tMitZuname").Value End If rs.MoveNext Loop Until rs.EOF MsgBox strListe, vbInformation, "Filter III" End If rs.Close Set rs = Nothing End Sub
Abbildung 5.13: Die ungefilterten Daten
188
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib
Drei Filter: Suche die Mitglieder, die mit „M“ beginnen, deren Mitgliedsbeitrag mehr als 179 (Euro) beträgt und die sowohl mit „M“ beginnen als auch mehr als 179 (Euro) Mitgliedsbeitrag bezahlen.
Abbildung 5.14: Die drei Filter
5.2.7 Daten ändern, hinzufügen und löschen Zwei der Hauptaufgaben neben der Anzeige von vorhandenen Daten sind das Verändern von Daten (dazu gehört auch das Löschen) und das Anhängen von neuen Daten. Hierzu stellt Access die drei Methoden Edit, AddNew und Delete zur Verfügung. Folgendes Programm fügt einen Datensatz an – unabhängig von der Position des Datensatzzeigers: Sub NeuerDatensatz() Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenKeyset rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" rs.AddNew rs.Fields("tMitVorname").Value = "Hans" rs.Fields("tMitZuname").Value = "Guck-in-die-Luft"
=XJULII DXV $FFHVV
189
Sandini Bib rs.Update rs.Close Set rs = Nothing End Sub
Die Methode Update ist notwendig, denn sie bewirkt letztendlich, dass der Datensatz dauerhaft hinzugefügt wird. Die Methode Update ist auch für das Ändern von Daten nötig: Sub DatensatzÄndern() Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenKeyset rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" rs.MoveLast rs.Fields("tMitVorname").Value = "Hans" rs.Fields("tMitZuname").Value = "Huckebein" rs.Update rs.Close Set rs = Nothing End Sub
Auch hier dürfen Sie die Methode Update nicht vergessen! Delete dagegen benötigt kein Update, wie das folgende Beispiel zeigt, in dem der letzte Datensatz (ohne Warnmeldung!) gelöscht wird: Sub DatensatzLöschen() Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenKeyset rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" rs.MoveLast rs.Delete rs.Close Set rs = Nothing End Sub
190
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib
5.2.8 Daten in eine XML-Datei schreiben Mit diesen wenigen, aber wichtigen Befehlen haben Sie nun das Handwerkszeug, um Daten aus einer Abfrage oder Tabelle auszulesen oder hineinzuschreiben. Ebenso wie im Beginn dieses Kapitels für Excel beschrieben, kann nun per Programmierung eine XML-Datei erzeugt werden: Sub TextStreamTest() Dim rs As ADODB.Recordset Dim fsDatei As Object Dim fsInhalt As Object Set rs = New ADODB.Recordset rs.Open "SELECT * FROM tab_Mitglieder", _ CurrentProject.Connection, adOpenStatic, _ adLockOptimistic, adCmdText ' -- der Datenbankzugriff Set fsDatei = CreateObject("Scripting.FileSystemObject") fsDatei.CreateTextFile _ "c:\Eigene Dateien\XML\Kapitel05\test03.xml" ' -- Erstellen einer Datei Set fsDatei = fsDatei.GetFile( _ "c:\Eigene Dateien\XML\Kapitel05\test03.xml") Set fsInhalt = fsDatei.OpenAsTextStream(2, -2) fsInhalt.write "" fsInhalt.write "" Do fsInhalt.write "" fsInhalt.write "" fsInhalt.write rs.Fields("tMitMitgliedsnummer").Value fsInhalt.write "" fsInhalt.write "" fsInhalt.write rs.Fields("tMitVorname").Value & " " & _ rs.Fields("tMitZuname").Value fsInhalt.write "" fsInhalt.write "" rs.MoveNext Loop While Not rs.EOF fsInhalt.write "" fsInhalt.Close rs.Close Set rs = Nothing
=XJULII DXV $FFHVV
191
Sandini Bib Set fsDatei = Nothing Set fsInhalt = Nothing End Sub
Diese kann nun im Internet Explorer geöffnet werden:
Abbildung 5.15: Die 3.777 Datensätze werden im Browser angezeigt
5.3
Übungen zu Kapitel 5
5.3.1 Excel Übung 1 Überprüfen Sie, ob die beiden Dateien „Test01.xls“ und „Test02.xls“ schon geöffnet sind. Falls eine der beiden Dateien noch nicht geöffnet ist, so wird der Benutzer darauf hingewiesen. Übung 2 Löschen Sie alle Tabellenblätter einer Mappe bis auf eines. Erzeugen Sie elf neue Blätter. Benennen Sie die zwölf Blätter „Januar“, „Februar“, ... „Dezember“.
192
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib
Übung 3 Überprüfen Sie, ob eines der vorhandenen Tabellenblätter der aktuellen Mappe „Sonstiges“ heißt. Falls es vorhanden ist, wird es aktiviert, falls nicht, erhält der Benutzer eine Meldung. Übung 4 In Zelle C1 steht eine Formel. Dieselbe Formel wird in F7 noch einmal benötigt. Würde man sie kopieren, würden die Bezüge nicht mehr stimmen. Deshalb soll die Formel so nach F7 kopiert werden, dass dieselben relativen Bezüge in dieser neuen Zelle stehen. Übung 5 In einer Arbeitsmappe wird in allen Tabellenblättern in der Zelle C27 die Summe der darüber stehenden Werte der Spalte C benötigt. Übung 6 Eine Mappe besteht aus mehreren Blättern. Auf jedem Blatt befindet sich in Zelle R37 ein Gesamtergebnis. Über eine Verknüpfung soll in jedem Blatt (beginnend ab dem zweiten) in der Zelle A1 Bezug auf die Zelle R37 des vorhergehenden Blatts genommen werden.
5.3.2 Access Übung 7 Eine Namensliste wird durchlaufen. Was zeigt das Meldungsfenster? Sub Übung() Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset Dim strListe As String Dim strSQL As String rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenKeyset rs.Open "SELECT * FROM tab_Mitglieder WHERE " & _ "tMitJahresbeitrag > 170" strSQL = "tMitPlz LIKE '6*'" rs.Filter = strSQL
hEXQJHQ ]X .DSLWHO
193
Sandini Bib Do If IsNull(rs.Fields("tMitZuname").Value) = False Then If Left(rs.Fields("tMitZuname").Value, 1) = "M" Then strListe = strListe & vbCr & rs.Fields("tMitZuname").Value End If End If rs.MoveNext Loop Until rs.EOF MsgBox strListe, vbInformation, "Filtert" rs.Close Set rs = Nothing End Sub
Übung 8 Was bewirkt die folgende Prozedur? Sub MalSehen() Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset With rs .ActiveConnection = CurrentProject.Connection .CursorType = adOpenKeyset .LockType = adLockOptimistic .Open "SELECT * FROM tab_Mitglieder" .AddNew .Fields("tMitVorname").Value = "Bilbo" .Fields("tMitZuname").Value = "Beutlin" End With End Sub
194
=XJULII DXI GLH 'DWHL PLW 9%$
Sandini Bib
6
le
XML-Dokumente mit VBA erzeugen
n e rn
In diesem Kapitel wird all das noch einmal gezeigt, was in Kapitel 1 ausführlich beschrieben wurde. Nur mit dem Unterscheid, dass ein XMLDokument nun nicht per Hand eingegeben, sondern programmiert wird. Das heißt: Die Daten befinden sich in einer Excel-Tabelle oder einer Access-Datenbank und werden nun mit Hilfe von VBA in ein wohlgeformtes XML-Dokument geschrieben. Außerdem wird der umgekehrte Weg beschrieben: Eine XML-Datei wird geöffnet und die Elemente werden in eine Datenbank oder Tabelle eingetragen. Und schließlich wird gezeigt, wie mit Hilfe von VBA ein Dokument auf Gültigkeit überprüft werden kann.
6.1
Von Excel mit VBA nach XML und zurück
6.1.1 Zugriff per VBA auf eine Excel-Mappe Per VBA auf die Excel-Liste zuzugreifen ist nicht sonderlich schwierig – es wurde in Kapitel 5 ausführlich beschrieben. Man setzt Verweise auf die Datei, auf das Tabellenblatt und lässt per Schleife alle Zellinhalte auslesen. Beispielsweise so: Sub Artikelliste_ZugriffaufExcel() Dim xlApp As Application Dim xlMappe As Workbook Dim xlBlatt As Worksheet Dim xlZelle As Range ' -- die benötigten Objektvariablen Dim lngZähler As Long Dim strInhalt As String ' -- die benötigten Variablen für die Schleife
9RQ ([FHO PLW 9%$ QDFK ;0/ XQG ]XUFN
195
Sandini Bib Set xlApp = Application Set xlMappe = xlApp.Workbooks("artikel-tabelle.xls") Set xlBlatt = xlMappe.Worksheets("Liste") Set xlZelle = xlBlatt.Range("E1") ' -- die Verweise werden gesetzt For lngZähler = 1 To xlZelle.CurrentRegion.Rows.Count - 1 strInhalt = strInhalt & vbCr & _ xlZelle.Offset(lngZähler, 0).Value Next ' -- eine Schleife durchläuft alle gefüllten Zellen der Spalte E MsgBox strInhalt End Sub
Abbildung 6.1: Eine Excelliste wird ausgelesen
Oder man lässt die Schleife mit Hilfe einer Objektvariablen durchlaufen: Sub Artikelliste_ZugriffaufExcel2() Dim xlApp As Application Dim xlMappe As Workbook Dim xlBlatt As Worksheet
196
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib Dim xlZelle As Range Dim xlLaufZelle As Range ' -- die benötigten Objektvariablen Dim strInhalt As String ' -- die benötigten Variablen Set xlApp = Application Set xlMappe = xlApp.Workbooks("artikel-tabelle.xls") Set xlBlatt = xlMappe.Worksheets("Liste") Set xlZelle = xlBlatt.Range("A1") ' -- die Verweise werden gesetzt For Each xlLaufZelle In _ xlZelle.CurrentRegion.Columns(5).Cells strInhalt = strInhalt & vbCr & xlLaufZelle.Value Next ' -- eine Schleife durchläuft alle gefüllten Zellen der fünften Spalte MsgBox strInhalt End Sub
6.1.2 Das DOM Nun sollen diese ermittelten Werte in ein XML-Dokument geschrieben werden. Zuerst müssen Sie einen Verweis auf das Microsoft XML-Objekt einbinden. Dies geschieht im Visual-Basic-Editor über das Menü EXTRAS/ VERWEISE. In Office 97 stand Ihnen „Microsoft XML“ in der Version 1.0 zur Verfügung, in Office 2000 die Version 2.0 beziehungsweise 2.6, seit Office 2002 auch die Version 3.0.
Abbildung 6.2: Zuerst muss ein Verweis gesetzt werden
9RQ ([FHO PLW 9%$ QDFK ;0/ XQG ]XUFN
197
Sandini Bib
Nun kann deklariert werden: Dim xml As New MSXML.DOMDocument
Wenn Sie das DOM Version 3.0 einbinden, dann lautet der Befehl: Dim xml As New MSXML2.DOMDocument30
Wenn Sie keinen Verweis einbinden wollen (oder können), dann können Sie sich das Objekt auch zur Laufzeit erzeugen lassen: Dim xml As Object Set xml = CreateObject("Microsoft.MXMLDOM")
Das Akronym DOM ist nun schon mehrmals aufgetaucht. DOM steht für „Document Object Model“. Damit wird das XML-Dokument wie ein Objekt behandelt und mit Hilfe von Methoden und Eigenschaften werden Informationen ausgelesen und hineingeschrieben. Und damit sind wir beim zweiten zentralen Thema des Buchs.
6.1.3 Ein neues XML-Dokument erzeugen Da man das XML-Dokument nicht direkt erzeugen will, sondern vom DOM generieren lassen möchte, muss das Dokument initialisiert und gespeichert werden. Die Methode loadXML initialisiert das DOM. Dies wird gespeichert, beispielsweise so: Dim xml As New MSXML.DOMDocument xml.loadXML "" xml.Save "c:\Eigene Dateien\XML\Kapitel06\test01.xml"
Abbildung 6.3: Das erste XML-Dokument – es ist noch minimal
198
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
Soll das XML-Dokument nicht gespeichert sondern weiterverarbeitet werden, dann speichert die Eigenschaft „xml“ die Zeichenkette zwischen. Man könnte also mit dem Meldungsfenster MsgBox xml.xml
das erzeugte XML-Dokument betrachten, ohne es zu speichern.
Abbildung 6.4: Das ganze XML-Dokument in einem Meldungsfenster
Wenn Sie die Versionsnummer in das Dokument einfügen möchten, dann können Sie dies wie folgt tun: xml.loadXML ""
oder Sie stellen die Anführungszeichen durch doppelte Anführungszeichen dar: xml.loadXML ""
Leider kann erst der XML-Parser MSXML 3.0 von Microsoft die korrekte Codierung vornehmen. Nur wenn Sie einen Verweis auf MSXML 3.0 gesetzt und folgende Variablen deklariert haben, kann die korrekte Codierung funktionieren: Dim xml As New MSXML2.DOMDocument Dim xmlKD As MSXML2.IXMLDOMElement xml.loadXML ""
Wie es dennoch funktioniert, wird weiter unten mit dem Befehl createProcessingInstruction erläutert.
Abbildung 6.5: Das Dokument mit korrekter PI
9RQ ([FHO PLW 9%$ QDFK ;0/ XQG ]XUFN
199
Sandini Bib
In der Datei befindet sich lediglich der Tag . Damit ist das Dokument sogar wohlgeformt, wie man leicht zeigen kann. Der Befehl xml.documentElement.baseName
liefert den korrekten Namen des Wurzelelements: „Buchliste“. Wäre kein Wurzelelement vorhanden (was gegen die Gültigkeitsregeln eines XML-Dokuments verstößt), dann würde „Null“ zurückgegeben werden. Will man dagegen eine vorhandene Datei öffnen (und nicht neu anlegen), so kann die Methode load verwendet werden: xml.Load "c:\Eigene Dateien\XML\Kapitel06\test02.xml"
Das Wurzelelement ist die schreibgeschützte Eigenschaft documentElement. Ihren Namen (baseName) kann man auslesen. Methoden und Eigenschaften der Klasse DOMDocument
Erläuterung
load
lädt ein XML-Dokument aus einer Datei
loadXML
lädt ein XML-Dokument aus einer Zeichenkette
save
speichert das DOM als XML-Datei
xml
der Textinhalt des XML-Dokuments
Tabelle 6.1: Die wichtigsten Methoden und Eigenschaften des DOMDocuments
6.1.4 Neue Elemente erzeugen Man kann mit Hilfe des DOM neue Elemente im XML-Dokument generieren lassen. Dazu wird deklariert: Dim xmlKD As MSXML.IXMLDOMElement
und anschließend erzeugt: Set xmlKD = xml.createElement("Artikel")
Das Erzeugen alleine genügt noch nicht – das neue Element muss noch angehängt werden: xml.documentElement.appendChild xmlKD
Die folgende Prozedur Sub Zugriff_Auf_XML01() Dim xml As New MSXML.DOMDocument Dim xmlKD As MSXML.IXMLDOMElement xml.loadXML "" Set xmlKD = xml.createElement("Artikel")
200
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib xml.documentElement.appendChild xmlKD xml.Save "c:\Eigene Dateien\XML\Kapitel06\test03.xml" End Sub
erzeugt das folgende XML-Dokument:
Abbildung 6.6: Das Wurzelelement hat nun ein Kindelement
Der Tag „Artikel“ ist (noch) leer. Soll er gefüllt werden, so muss der Inhalt über die Eigenschaft „Text“ hinzugefügt werden: Sub Zugriff_Auf_XML02() Dim xml As New MSXML.DOMDocument Dim xmlKD As MSXML.IXMLDOMElement xml.loadXML "" Set xmlKD = xml.createElement("Artikel") xmlKD.Text = "Die letzten Tage von Pompeji" xml.documentElement.appendChild xmlKD xml.Save "c:\Eigene Dateien\XML\Kapitel06\test04.xml" End Sub
Das Ergebnis sieht nun bekannt aus:
9RQ ([FHO PLW 9%$ QDFK ;0/ XQG ]XUFN
201
Sandini Bib Die letzten Tage von Pompeji
Ebenso können weitere Elemente eingelesen werden: [...] Set xmlKD = xml.createElement("Artikel") xmlKD.Text = "Per Anhalter durch die Galaxis" xml.documentElement.appendChild xmlKD Set xmlKD = xml.createElement("Artikel") xmlKD.Text = "Puppenmord" xml.documentElement.appendChild xmlKD [...]
Abbildung 6.7: Das XML-Dokument wächst
Und selbstverständlich auch Unterelemente: Sub Zugriff_Auf_XML04() Dim xml As New MSXML.DOMDocument Dim xmlKD As MSXML.IXMLDOMElement Dim xmlUnterKD1 As MSXML.IXMLDOMElement Dim xmlUnterKD2 As MSXML.IXMLDOMElement xml.loadXML "" Set xmlKD = xml.createElement("Artikel") Set xmlUnterKD1 = xml.createElement("Titel") Set xmlUnterKD2 = xml.createElement("Autor")
202
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib xmlKD.appendChild(xmlUnterKD1).Text = _ "Per Anhalter durch die Galaxis" xmlKD.appendChild(xmlUnterKD2).Text = "Douglas Adams" Set xmlUnterKD1 = xml.createElement("Titel") Set xmlUnterKD2 = xml.createElement("Autor") xmlKD.appendChild(xmlUnterKD1).Text = "Puppenmord" xmlKD.appendChild(xmlUnterKD2).Text = "Tom Sharpe" xml.documentElement.appendChild xmlKD xml.Save "c:\Eigene Dateien\XML\Kapitel06\test06.xml" End Sub
Oder vernünftiger natürlich so: Sub Zugriff_Auf_XML05() Dim xml As New MSXML.DOMDocument Dim xmlKD As MSXML.IXMLDOMElement Dim xmlUnterKD1 As MSXML.IXMLDOMElement Dim xmlUnterKD2 As MSXML.IXMLDOMElement xml.loadXML "" Set xmlKD = xml.createElement("Artikel") Set xmlUnterKD1 = xml.createElement("Titel") Set xmlUnterKD2 = xml.createElement("Autor") xmlKD.appendChild(xmlUnterKD1).Text = _ "Per Anhalter durch die Galaxis" xmlKD.appendChild(xmlUnterKD2).Text = "Douglas Adams" xml.documentElement.appendChild xmlKD Set xmlKD = xml.createElement("Artikel") Set xmlUnterKD1 = xml.createElement("Titel") Set xmlUnterKD2 = xml.createElement("Autor") xmlKD.appendChild(xmlUnterKD1).Text = "Puppenmord" xmlKD.appendChild(xmlUnterKD2).Text = "Tom Sharpe" xml.documentElement.appendChild xmlKD xml.Save "c:\Eigene Dateien\XML\Kapitel06\test07.xml" End Sub
Übrigens wurde hier leichtfertig mit der Eigenschaft Text umgegangen. Beim Schreiben von Daten ist es korrekt, nur beim Auslesen von Inhal-
9RQ ([FHO PLW 9%$ QDFK ;0/ XQG ]XUFN
203
Sandini Bib
ten sollten Sie nodeValue verwenden, da Text auch die Unterelemente anzeigt. Das Ergebnis gestaltet sich wie folgt: Per Anhalter durch die Galaxis Douglas Adams Puppenmord Tom Sharpe
Abbildung 6.8: Das aktuelle XML-Dokument
Und damit wird klar, wie nun die Exceltabelle exportiert werden kann: Sub Export03() Dim xml As New MSXML.DOMDocument Dim xmlKD As MSXML.IXMLDOMElement Dim xmlUnterKD1 As MSXML.IXMLDOMElement Dim xmlUnterKD2 As MSXML.IXMLDOMElement Dim xlApp As Application Dim xlMappe As Workbook Dim xlBlatt As Worksheet Dim xlZelle As Range
204
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib ' -- die benötigten Objektvariablen Dim lngZähler As Long Dim strInhalt As String ' -- die benötigten Variablen Set xlApp = Application Set xlMappe = xlApp.Workbooks("artikel-tabelle.xls") Set xlBlatt = xlMappe.Worksheets("Liste") Set xlZelle = xlBlatt.Range("C1") ' -- die Verweise werden gesetzt xml.loadXML "" For lngZähler = 1 To xlZelle.CurrentRegion.Rows.Count - 1 Set xmlKD = xml.createElement("Buch") ' -- ein neues Element wird angelegt xmlKD.setAttribute "Preis", xlZelle.Offset _ (lngZähler, 3).Value ' -- ein Attribut wird gesetzt Set xmlUnterKD1 = xml.createElement(xlZelle.Offset(0, -1).Value) Set xmlUnterKD2 = xml.createElement _ (xlZelle.Offset(0, 0).Value) ' -- das Element "Buch" hat zwei Unterelemente ' -- ihr Name wird aus der Tabelle ausgelesen xmlKD.appendChild(xmlUnterKD1).Text = _ xlZelle.Offset(lngZähler, -1).Value xmlKD.appendChild(xmlUnterKD2).Text = _ xlZelle.Offset(lngZähler, 0).Value ' -- der Inhalt der Unterelemente xml.documentElement.appendChild xmlKD Next ' -- eine Schleife durchläuft alle gefüllten Zellen der Spalte C xml.Save "c:\Eigene Dateien\XML\Kapitel06\test08.xml" End Sub
9RQ ([FHO PLW 9%$ QDFK ;0/ XQG ]XUFN
205
Sandini Bib
Abbildung 6.9: Die Daten wurden von Excel exportiert
Und so können problemlos neue Elemente an ein bestehendes XMLDokument angefügt werden: Sub NeuesElement() Dim xml As New MSXML.DOMDocument Dim xmlUnterKD1 As MSXML.IXMLDOMElement Dim xmlUnterKD2 As MSXML.IXMLDOMElement Dim xmlKD As MSXML.IXMLDOMElement xml.Load "c:\Eigene Dateien\XML\Kapitel06\test07.xml " Set xmlKD = xml.createElement("Artikel") Set xmlUnterKD1 = xml.createElement("Titel") Set xmlUnterKD2 = xml.createElement("Autor") xmlKD.appendChild(xmlUnterKD1).Text = _ "Kishons beste Familiengeschichten" xmlKD.appendChild(xmlUnterKD2).Text = "Ephraim Kishon" xml.documentElement.appendChild xmlKD xml.Save "c:\Eigene Dateien\XML\Kapitel06\test09.xml " End Sub
206
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
Abbildung 6.10: Am Ende des Dokuments wird ein neues Element hinzugefügt
6.1.5 XML-Dokumente auslesen Und damit steht schon fast das Gerüst, mit dessen Hilfe die XML-Datei ausgelesen werden kann. Zuerst wird der Name des Wurzelelements ermittelt. Wenn Sie vertraut sind mit den „klassischen“ VBA-Befehlen, dann kennen Sie sicherlich den Befehl Open. Er arbeitet beispielsweise wie folgt: Private Function XMLZugriff(strFilename As String) As String Dim strXML As String Open strFilename For Binary As #1 strXML = Space(LOF(1)) Get #1, , strXML Close #1 XMLZugriff = strXML End Function
Einfacher geht es sicherlich mit dem Befehl load: xml.Load "c:\Eigene Dateien\XML\Kapitel06\test09.xml"
Dann kann auf das Wurzelelement zugegriffen werden: Dim strWurzel As String strWurzel = xml.documentElement.baseName
9RQ ([FHO PLW 9%$ QDFK ;0/ XQG ]XUFN
207
Sandini Bib
oder auch so: strWurzel = xml.childNodes(0).baseName
Und wie viele Elemente besitzt das Wurzelelement? VBA-Programmierer kennen den Befehl count, das W3C hat length festgelegt: xml.documentElement.childNodes.Length
Und auf diese kann einzeln zugegriffen werden: For lngZähler = 0 To xml.documentElement.childNodes.Length - 1 MsgBox xml.documentElement.childNodes(lngZähler).Text Next
Achtung: Die Zählung beginnt bei 0!
Die Eigenschaft length ist wichtig, da man mit ihrer Hilfe ermitteln kann, ob überhaupt Kindelemente (oder Attribute) vorhanden sind. Alternativ kann die Methode hasChildNodes verwendet werden. Da das Tag „Buch“ zwei Unterelemente besitzt, nämlich „Artikelname“ und „Warenhauptgruppe“, werden sämtliche, das heißt hier beide, Texte ausgelesen und aneinander gehängt. Um dies zu vermeiden, wird eine weitere Schleife benötigt, die auf jedes Element zugreift: For lngZähler = 0 To xml.documentElement.childNodes.Length - 1 For lngUnterZähler = 0 To _ xml.documentElement.childNodes(lngZähler). _ childNodes.Length - 1 MsgBox xml.documentElement.childNodes(lngZähler). _ childNodes(lngUnterZähler).Text Next lngUnterZähler Next lngZähler
Und diese können in eine Excel-Tabelle zurückgeschrieben werden: Sub Import02() Dim xml As New MSXML.DOMDocument Dim xlApp As Application Dim xlMappe As Workbook Dim xlBlatt As Worksheet Dim xlZelle As Range ' -- die benötigten Objektvariablen Dim strWurzel As String Dim lngZähler As Long
208
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib Dim lngUnterZähler As Long ' -- die benötigten Variablen Set xlApp = Application Set xlMappe = xlApp.Workbooks.Add Set xlBlatt = xlMappe.Worksheets(1) Set xlZelle = xlBlatt.Range("A1") ' -- die Verweise werden gesetzt xml.Load "c:\Eigene Dateien\XML\Kapitel06\test08.xml" ' -- die Datei wird geöffnet xlBlatt.Name = xml.documentElement.baseName ' -- das erste Tabellenblatt erhält einen Namen For lngUnterZähler = 0 To _ xml.documentElement.childNodes(0).childNodes.Length - 1 xlZelle.Offset(0, lngUnterZähler).Value = _ xml.documentElement.childNodes(0).childNodes _ (lngUnterZähler).baseName Next lngUnterZähler ' -- die erste Zeile wird beschriftet For lngZähler = 0 To _ xml.documentElement.childNodes.Length - 1 For lngUnterZähler = 0 To _ xml.documentElement.childNodes(lngZähler) _ .childNodes.Length - 1 xlZelle.Offset(lngZähler + 1, lngUnterZähler).Value = _ xml.documentElement.childNodes(lngZähler). _ childNodes(lngUnterZähler).Text Next lngUnterZähler Next lngZähler ' -- die Werte werden eingetragen Set xml = Nothing Set xlZelle = Nothing Set xlBlatt = Nothing Set xlMappe = Nothing Set xlApp = Nothing ' -- die Objektvariablen werden geleert End Sub
9RQ ([FHO PLW 9%$ QDFK ;0/ XQG ]XUFN
209
Sandini Bib
Abbildung 6.11: Von XML nach Excel
Oder – etwas eleganter – mit einer Schleife über die einzelnen Objekte: sub XMLImport2 Dim xml As New MSXML.DOMDocument Dim xmlKD As MSXML.IXMLDOMElement Dim xmlUnterKD As MSXML.IXMLDOMElement Dim xlApp As Application Dim xlMappe As Workbook Dim xlBlatt As Worksheet Dim xlZelle As Range ' -- die benötigten Objektvariablen Dim strWurzel As String Dim lngZähler As Long Dim lngUnterZähler As Long ' -- die benötigten Variablen [...] lngUnterZähler = 0 For Each xmlKD In _ xml.documentElement.childNodes(0).childNodes xlZelle.Offset(0, lngUnterZähler).Value = _ xmlKD.baseName
210
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib lngUnterZähler = lngUnterZähler + 1 Next xmlKD ' -- die erste Zeile wird beschriftet lngZähler = 0 For Each xmlKD In xml.documentElement.childNodes lngUnterZähler = 0 For Each xmlUnterKD In xmlKD.childNodes xlZelle.Offset(lngZähler + 1, lngUnterZähler).Value = _ xmlUnterKD.Text lngUnterZähler = lngUnterZähler + 1 Next xmlUnterKD lngZähler = lngZähler + 1 Next xmlKD ' -- die Werte werden eingetragen [...]
6.1.6 Zugriff auf Attribute Auf diese Weise können ebenfalls Attribute ausgelesen werden. Das folgende Beispiel liefert den Namen des Attributs („Preis“): Set xmlKD = xml.documentElement.childNodes(0) xlZelle.Offset(0, lngUnterZähler).Value = _ xmlKD.Attributes(0).baseName
Oder man durchläuft mit einer Schleife alle Elemente und greift sich den Wert des Attributes heraus: For Each xmlKD In xml.documentElement.childNodes xlZelle.Offset(lngZähler + 1, 2).Value = _ xmlKD.Attributes(0).Text lngUnterZähler = lngUnterZähler + 1 lngZähler = lngZähler + 1 Next xmlKD ' -- die Attribute werden eingetragen
Wenn man die Namen der Attribute kennt, so kann man über ihren Namen getAttribute mit der Methode auf sie zugreifen: xmlKD.getAttribute("Preis")
Oder den Namen ermitteln lassen: strAttributname = xmlKD.Attributes(0).baseName xlZelle.Offset(lngZähler + 1, 2).Value = _ xmlKD.getAttribute(strAttributname)
9RQ ([FHO PLW 9%$ QDFK ;0/ XQG ]XUFN
211
Sandini Bib
In Excel liefert der Befehl xlZelle.CurrentRegion.Columns.Count
die Nummer der letzten Spalte (hier: 3). Damit kann die letzte Spalte formatiert werden: xlZelle.CurrentRegion.Columns _ (xlZelle.CurrentRegion.Columns.Count) _ .NumberFormatLocal = "#.##0,00"
Alle Spalten erhalten nun ihre optimale Breite: xlZelle.CurrentRegion.EntireColumn.AutoFit
Und damit werden auch die Attribute ausgelesen:
Abbildung 6.12: Nach Excel werden die Inhalte und die Attribute exportiert
Methoden und Eigenschaften der Klasse DOMDocument
Erläuterung
documentElement
das Wurzelelement
createElement
erzeugt ein neues Element
Tabelle 6.2: Weitere Methoden und Eigenschaften der Klasse DOMDocument
212
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib Methoden und Eigenschaften der Klasse IXMLDOMElement
Erläuterung
appendChild
hängt ein neues Element an
length
die Anzahl der Elemente einer Liste
text
Inhalt des Elements
baseName
der Name des Elements oder Attributs
nodeValue
der Name des Knotens (entspricht baseName)
tagName
der Name des Elements (entspricht baseName)
setAttribute
setzt den Wert eines Attributs
getAttribute
liest den Wert des Attributs
getAttributeNode
das Attribut wird als Objekt zurückgegeben
item
ein Element der Liste Alternative Schreibweisen sind: xml.documentElement.childNodes(0) xml.documentElement.childNodes.item(0)
Tabelle 6.3: Die wichtigsten Methoden und Eigenschaften der Klasse IXMLDOMElement
6.2
Durchlaufen von mehreren Ebenen
Schwieriger wird es, wenn keine oder keine vollständigen Informationen über das XML-Dokument vorliegen, das heißt, wenn nicht bekannt ist, wie viele Ebenen das Dokument verwendet. Dann muss es von der ersten bis zur letzten Ebene durchlaufen werden. Eine Abfrage überprüft, ob es sich bei einem Element um einen Knoten handelt. Falls ja, dann wird diese Prozedur erneut aufgerufen. Falls nein, dann wird der Text des Elements aufgerufen. Diese Funktion, die sich selbst aufruft, wird von außen angestoßen und verwendet sich selbst als Möglichkeit. Dies könnte so aussehen: Sub MeinEigenesXML() Dim xml As New MSXML.DOMDocument Dim xmlElement As MSXML.IXMLDOMElement xml.Load "C:\Eigene Dateien\XML\Kapitel06\test08.xml" strText = xml.documentElement.baseName & vbCr Set xmlElement = xml.documentElement Call XMLDurchlauf(xmlElement)
'XUFKODXIHQ YRQ PHKUHUHQ (EHQHQ
213
Sandini Bib MsgBox strText End Sub Sub XMLDurchlauf(ByVal xmlElement As MSXML.IXMLDOMElement) Dim i As Integer For i = 0 To xmlElement.childNodes.Length - 1 With xmlElement.childNodes(i) If .nodeType = NODE_ELEMENT Then strText = strText & _ xmlElement.childNodes(i).nodeName & ": " XMLDurchlauf xmlElement.childNodes(i) ElseIf .nodeType = NODE_TEXT Then strText = strText & .nodeValue & vbCr Else strText = strText & " ** ?? ** " End If End With Next i End Sub
Abbildung 6.13: Das Ergebnis nach dem Zugriff auf eine „beliebige“ Datei
Angenommen einige Dinge sind über das XML-Dokument bekannt – sei es die maximale Tiefe der Ebenen, die maximale Anzahl der Knoten oder Ähnliches, so kann man sich sicherlich leichter durch ein XMLDokument bewegen. Das folgende Dokument ist sehr uneinheitlich aufgebaut:
214
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib Der Kaufmann von Venedig William Shakespeare Zuckerschlampen Rosenstolz Zucker Die öffentliche Frau Die Schlampen sind müde Die Zigarette danach The Jungle Book 78 min. 1967 Colonel Hathi's March The Bare Necessities I Wan'na Be Like You Trust In Me My Own Home
Abbildung 6.14: Das unübersichtliche XML-Dokument
'XUFKODXIHQ YRQ PHKUHUHQ (EHQHQ
215
Sandini Bib
6.3
Zwischenergebnis
Der Zugriff auf das oben beschriebene Dokument ist sicherlich nicht trivial. Im Folgenden sollen die Möglichkeiten des Auslesens der Informationen einer XML-Datei zusammengefasst werden. Zuerst wird ein Dokument geladen: Sub XML_Zugriff() Dim xml As New MSXML.DOMDocument xml.Load "c:\Eigene Dateien\XML\Kapitel06\test10.xml"
Wird die mögliche Existenz des Dokuments angezweifelt, das heißt, soll überprüft werden, ob das Dokument überhaupt ein Element besitzt, dann kann das wie folgt überprüft werden: If xml.xml = "" Then MsgBox "Dokument ist leer" Exit Sub End If
Danach wird auf das Wurzelelement zugegriffen. Es ist vom Typ „Element“ und wir können sicher sein, dass es existiert. Sein Name und die Anzahl und Namen seiner Attribute werden mit ihrem Inhalt angezeigt: Dim xmlElement As MSXML.IXMLDOMElement Dim xmlAttribut As MSXML.IXMLDOMAttribute [...] Set xmlElement = xml.documentElement MsgBox xmlElement.baseName MsgBox xmlElement.Attributes.Length For Each xmlAttribut In xmlElement.Attributes MsgBox xmlAttribut.baseName & ": " & _ xmlAttribut.Value Next
Und so können nun alle Kindelemente durchlaufen werden. Sollten sie Attribute besitzen, wie beispielsweise das Element „DVD“, dann werden diese angezeigt: Dim xmlKnoten As MSXML.IXMLDOMNode Dim strText As String [...] For Each xmlKnoten In xmlElement.childNodes strText = "" strText = xmlKnoten.baseName If xmlKnoten.Attributes.Length > 0 Then strText = strText & vbCr & "Attribute:" End If For Each xmlAttribut In xmlKnoten.Attributes
216
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib strText = strText & vbCr & xmlAttribut.Value Next MsgBox strText Next
Wie kann man allerdings in anderer Reihenfolge die Elemente durchlaufen? Von hinten nach vorne? Oder in einer beliebigen Reihenfolge? Die Anzahl der Knoten beträgt: MsgBox xmlElement.childNodes.Length
Angenommen, die Elemente sollen nach folgender Regel sortiert werden: Zeige zuerst das erste Element, dann das letzte, anschließend das zweite, daraufhin das vorletzte, danach das dritte und so weiter. Dann könnte man einen Zähler hochlaufen lassen. Ist der Zählerwert gerade, so wird zur Gesamtanzahl eins addiert und davon die Hälfte des Zählerwerts abgezogen. Ist der Zählerwert ungerade, so wird zum Zählerwert eins addiert und das Ergebnis durch zwei geteilt. Also folgendermaßen: Dim intZähler As Integer Dim intWert As Integer [...] strText = "" For intZähler = 1 To xmlElement.childNodes.Length If intZähler / 2 = Int(intZähler / 2) Then intWert = xmlElement.childNodes.Length + _ 1 - intZähler / 2 Else intWert = (intZähler + 1) / 2 End If strText = strText & vbCr & _ xmlElement.childNodes(intWert - 1).baseName Next MsgBox strText
Abbildung 6.15: Der geänderte Zugriff
Das gleiche Ergebnis erhält man mit folgender Prozedur: Dim xmlElementVonVorn As MSXML.IXMLDOMElement Dim xmlElementVonHinten As MSXML.IXMLDOMElement
=ZLVFKHQHUJHEQLV
217
Sandini Bib [...] strText = "" Set xmlElementVonVorn = xmlElement.firstChild Set xmlElementVonHinten = xmlElement.lastChild strText = xmlElementVonVorn.baseName & _ vbCr & xmlElementVonHinten.baseName For intZähler = 2 To xmlElement.childNodes.Length - 1 If intZähler / 2 = Int(intZähler / 2) Then Set xmlElementVonHinten = _ xmlElementVonHinten.previousSibling strText = strText & vbCr & _ xmlElementVonHinten.baseName Else Set xmlElementVonVorn = _ xmlElementVonVorn.nextSibling strText = strText & vbCr & _ xmlElementVonVorn.baseName End If Next MsgBox strText
Hier wurden folgende Eigenschaften verwendet: Eigenschaften des Wurzelelements und der Knoten
Erläuterung
firstChild
das erste Kindelement
lastChild
das letzte Kindelement
previousSibling
der vorhergehende Knoten
nextSibling
der nächste Knoten
parentNode
der direkte Vorfahre (das Dokument selbst besitzt keinen parentNode, der parentNode des Wurzelknotens ist leer)
Tabelle 6.4: Der erste, letzte, vorhergehende, nächste und darüberliegende Knoten
Mit diesen Mitteln kann man nun mit Hilfe von rekursiver Programmierung die Werte strukturiert in eine Excel-Tabelle einlesen: Option Dim Dim Dim
Explicit intSpalten As Integer intZeilen As Integer xlZelle As Range
Sub XML_Zugriff() Dim xml As New MSXML.DOMDocument Dim xmlElement As MSXML.IXMLDOMElement
218
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib Dim xlApp As Application Dim xlMappe As Workbook Dim xlBlatt As Worksheet ' -- die benötigten Objektvariablen
xml.Load "c:\Eigene Dateien\XML\Kapitel06\test10.xml" If xml.xml = "" Then MsgBox "Dokument ist leer" Exit Sub End If Set xmlElement = xml.documentElement ' -- das Wurzelelement
Set xlApp = Application Set xlMappe = xlApp.Workbooks.Add Set xlBlatt = xlMappe.Worksheets(1) Set xlZelle = xlBlatt.Range("A1") ' -- die Verweise werden gesetzt xlBlatt.Name = xmlElement.baseName ' -- das erste Tabellenblatt erhält einen Namen
intZeilen = 0 intSpalten = 0
Call Durchlauf(xmlElement) Set Set Set Set Set Set
Sub Durchlauf(xmlElement As MSXML.IXMLDOMElement) Dim strText As String Dim strAttribute As String Dim xmlKnoten As MSXML.IXMLDOMNode Dim xmlAttribut As MSXML.IXMLDOMAttribute
=ZLVFKHQHUJHEQLV
219
Sandini Bib For Each xmlKnoten In xmlElement.childNodes strText = "" If xmlKnoten.nodeType = NODE_ELEMENT Then strText = xmlKnoten.baseName If xmlKnoten.Attributes.Length > 0 Then strAttribute = "Attribute:" End If For Each xmlAttribut In xmlKnoten.Attributes strAttribute = strAttribute & Chr(10) & _ xmlAttribut.Value Next ElseIf xmlKnoten.nodeType = NODE_TEXT Then strText = xmlKnoten.nodeValue Else strText = "Nicht erkennbar" End If
xlZelle.Offset(intZeilen, intSpalten).Value = strText If strAttribute "" Then xlZelle.Offset(intZeilen, intSpalten).AddComment strAttribute End If
If xmlKnoten.nodeType = NODE_ELEMENT Then intSpalten = intSpalten + 1 Call Durchlauf(xmlKnoten) End If Next intSpalten = intSpalten - 1 intZeilen = intZeilen + 1 End Sub
Das Beispiel funktioniert ähnlich wie das oben gezeigte – nur mit dem Unterschied, dass nun die Werte strukturiert nach Excel geschrieben werden.
220
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
Abbildung 6.16: Die Daten werden in Excel nun anders aufgelistet
6.4
Weitere XML-Elemente in VBA
6.4.1 Lesen der XML-Objekte und ihrer Eigenschaften Anhand einiger Beispiele werden systematisch die Eigenschaften und Methoden der Objektbibliothek „MSXML“ aufgelistet. Gegeben sei ein XML-Dokument mit folgender Gestalt: ]> Hofstadter
:HLWHUH ;0/(OHPHQWH LQ 9%$
221
Sandini Bib Douglas R. Gardner Martin Ifrah Georges
Abbildung 6.17: Ein einfaches Dokument
Schon vor dem Laden kann die Eigenschaft async auf True oder False gesetzt werden, je nachdem, ob während des Ladevorgangs schon weitergearbeitet werden soll oder nicht. Geladen wird es mit x0.Load "c:\Eigene Dateien\XML\Beispiele\test27.xml"
wenn x0 deklariert wurde: Dim x0 As New MSXML.DOMDocument
Der DTD entspricht das Objekt doctype – eine Eigenschaft des Dokuments. Man kann beispielsweise den Inhalt mit
222
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib MsgBox x0.doctype.xml
auslesen. Leider ist dieses Objekt schreibgeschützt. Es ist also nicht möglich, per Programmierung dem Dokument eine DTD hinzuzufügen. Würde ein Namensraum verwendet werden, dann könnte man dies mit der Eigenschaft namespaceURI
ermitteln: If x0.namespaceURI = "" Then MsgBox "Kein Namensraum angegeben." Else MsgBox "Der Namensraum lautet: " & x0.namespaceURI End If
Auch diese Eigenschaft ist, wie die DTD, schreibgeschützt. Es ist also nicht möglich, eine DTD hinzuzufügen. Existiert ein Namensraum, so wird das Präfix mit der Eigenschaft prefix herausgelöst. Das Dokument selbst besitzt 4 Knoten, wie mit der Eigenschaft x0.childNodes.Length
leicht ermittelt werden kann. Jeder Knoten besitzt die Eigenschaft nodeType, über welchen er näher bestimmt werden kann. Dabei entsprechen: Knotentyp
Nummer
NODE_INVALID
0
NODE_ELEMENT
1
NODE_ATTRIBUTE
2
NODE_TEXT
3
NODE_CDATA_SECTION
4
NODE_ENTITY_REFERENCE
5
NODE_ENTITY
6
NODE_PROCESSING_INSTRUCTION
7
NODE_COMMENT
8
NODE_DOCUMENT
9
NODE_DOCUMENT_TYPE
10
NODE_DOCUMENT_FRAGMENT
11
NODE_NOTATION
12
Tabelle 6.5: Die verschiedenen Knotenarten
Diese Liste finden Sie auch im Objektkatalog in VBA. Diese Eigenschaft ist schreibgeschützt, was bedeutet, dass man nicht per Programmierung
:HLWHUH ;0/(OHPHQWH LQ 9%$
223
Sandini Bib
den Typ ändern kann, was vernünftig ist. Mit der Eigenschaft nodeType kann überprüft werden: Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim
x0 As New MSXML.DOMDocument xmlKnoten As MSXML.IXMLDOMNode x1 As MSXML.IXMLDOMElement x2 As MSXML.IXMLDOMAttribute x3 As MSXML.IXMLDOMText x4 As MSXML.IXMLDOMCDATASection x5 As MSXML.IXMLDOMEntityReference x6 As MSXML.IXMLDOMEntity x7 As MSXML.IXMLDOMProcessingInstruction x8 As MSXML.IXMLDOMComment x9 As MSXML.XMLDocument x10 As MSXML.IXMLDOMDocumentType x11 As MSXML.IXMLDOMDocumentFragment x12 As MSXML.IXMLDOMNotation
Dim strAusgabe As String x0.Load "c:\Eigene Dateien\XML\Kapitel06\test12.xml" For Each xmlKnoten In x0.childNodes Select Case xmlKnoten.nodeType Case NODE_ELEMENT Set x1 = xmlKnoten strAusgabe = x1.baseName & ": Case NODE_ATTRIBUTE Set x2 = xmlKnoten strAusgabe = x2.baseName & ": Case NODE_TEXT Set x3 = xmlKnoten strAusgabe = x3.baseName & ": Case NODE_CDATA_SECTION Set x4 = xmlKnoten strAusgabe = x4.baseName & ": Case NODE_ENTITY_REFERENCE Set x5 = xmlKnoten strAusgabe = x5.baseName & ": Case NODE_ENTITY Set x6 = xmlKnoten strAusgabe = x6.baseName & ": Case NODE_PROCESSING_INSTRUCTION Set x7 = xmlKnoten strAusgabe = x7.Text Case NODE_COMMENT Set x8 = xmlKnoten
224
" & vbCr & x1.Text
" & vbCr & x2.Text
" & vbCr & x3.Text
" & vbCr & x4.Text
" & vbCr & x5.Text
" & vbCr & x6.Text
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib strAusgabe = x8.Text Case NODE_DOCUMENT Set x9 = xmlKnoten strAusgabe = x9.baseName & ": " & vbCr & x9.Text Case NODE_DOCUMENT_TYPE Set x10 = xmlKnoten strAusgabe = x10.baseName Case NODE_DOCUMENT_FRAGMENT Set x11 = xmlKnoten strAusgabe = x11.baseName & ": " & vbCr & x11.Text Case NODE_NOTATION Set x12 = xmlKnoten strAusgabe = x12.baseName & ": " & vbCr & x12.Text Case Else strAusgabe = "Unbekannter Knoten" End Select MsgBox strAusgabe Next
Abbildung 6.18: Die Processing Instruction (PI) wird ausgelesen
Abbildung 6.19: Der Kommentar wird ausgelesen
Abbildung 6.20: Das Wurzelelement wird ausgelesen
Abbildung 6.21: Die Kindelemente werden ausgelesen
:HLWHUH ;0/(OHPHQWH LQ 9%$
225
Sandini Bib
Im Fall des obigen Beispiels werden die Inhalte der vier Knoten x7, x8, x10 und x1 ausgegeben: „xml version="1.0" encoding="ISO-8859-1"“, „**** Dieses Dokument ist für die deutsche Sprache geeignet und folgt den Konventionen der Version 1.0. ****“, „Autorenliste“ und „Autorenliste: Hofstadter Douglas R. Gardner Martin Ifrah Georges“. Wenn Sie lieber mit Zeichenketten arbeiten, dann können Sie den Typ des Knotens als String mit der Eigenschaft nodeTypeString ermitteln. Dabei stehen folgende nodeTypeString-Werte zur Verfügung: element, attribute, text, cdatasection, entityreference, processinginstruction, comment, documenttype und documentfragment. Die Alternative sieht wie folgt aus: Sub ElementtypenPrüfung_MitString() Dim x0 As New MSXML.DOMDocument Dim xmlKnoten As MSXML.IXMLDOMNode Dim x1 As MSXML.IXMLDOMElement Dim x2 As MSXML.IXMLDOMAttribute Dim x3 As MSXML.IXMLDOMText Dim x4 As MSXML.IXMLDOMCDATASection Dim x5 As MSXML.IXMLDOMEntityReference Dim x7 As MSXML.IXMLDOMProcessingInstruction Dim x8 As MSXML.IXMLDOMComment Dim x9 As MSXML.XMLDocument Dim x10 As MSXML.IXMLDOMDocumentType Dim x11 As MSXML.IXMLDOMDocumentFragment Dim x12 As MSXML.IXMLDOMNotation Dim strAusgabe As String x0.Load "c:\Eigene Dateien\XML\Beispiele\test27.xml" For Each xmlKnoten In x0.childNodes Select Case xmlKnoten.nodeTypeString Case "element" Set x1 = xmlKnoten strAusgabe = x1.baseName & ": " & Case "attribut" Set x2 = xmlKnoten strAusgabe = x2.baseName & ": " & Case "text" Set x3 = xmlKnoten strAusgabe = x3.baseName & ": " & Case "cdatasection" Set x4 = xmlKnoten strAusgabe = x4.baseName & ": " &
226
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
vbCr & x1.Text
vbCr & x2.Text
vbCr & x3.Text
vbCr & x4.Text
Sandini Bib Case "entityreference" Set x5 = xmlKnoten strAusgabe = x5.baseName & ": " & vbCr & x5.Text Case "processinginstruction" Set x7 = xmlKnoten strAusgabe = x7.Text Case "comment" Set x8 = xmlKnoten strAusgabe = x8.Text Case "documenttype" Set x10 = xmlKnoten strAusgabe = x10.baseName Case "documentfragment" Set x11 = xmlKnoten strAusgabe = x11.baseName & ": " & vbCr & x11.Text Case Else strAusgabe = "Unbekannter Knoten" End Select MsgBox strAusgabe Next End Sub
Übrigens: In der XML-Spezifikation ist vorgesehen, dass alle Leerräume erhalten bleiben. Besonders für Kommentare ist dies wichtig, wenn sie aus mehreren Wörtern bestehen. Soll dies explizit festgelegt (oder unterbunden) werden, dann ist hierfür die Eigenschaft preserveWhiteSpace verantwortlich. Wird sie auf „True“ gesetzt, dann bleiben alle Leerräume, das heißt alle Leerzeichen, Tabulatoren, Absatzwechsel und Zeilenschaltungen erhalten.
6.4.2 Erzeugen von XML-Objekten und Eigenschaften Mit den beiden Objekten „Element“ und „Node“ können neue Elemente erzeugt und mit Eigenschaften versehen werden. Dim Dim Dim Dim Dim
x0 x1 x2 x3 x4
As As As As As
New MSXML.DOMDocument MSXML.IXMLDOMElement MSXML.IXMLDOMElement MSXML.IXMLDOMNode MSXML.IXMLDOMNode
Dim lngZähler As Long Dim strAusgabe As String x0.Load "c:\Eigene Dateien\XML\Kapitel06\test13.xml"
:HLWHUH ;0/(OHPHQWH LQ 9%$
227
Sandini Bib Set x1 = x0.documentElement Set x2 = x0.createElement("Autor") Set x3 = x1.appendChild(x2) Set x3 = x0.createElement("Zuname") Set x4 = x2.appendChild(x3) x4.Text = "Hawkins" Set x3 = x0.createElement("Vorname") Set x4 = x2.appendChild(x3) x4.Text = "Steve" x0.Save x0.URL
Abbildung 6.22: Vorher ...
Abbildung 6.23: ... und danach
228
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
Damit der Pfad beim Speichern nicht ein zweites Mal hergeholt werden muss, kann der Speicherort mit der Eigenschaft URL ermittelt werden. Da nicht sicher ist, ob ein Element ein Attribut besitzt, muss überprüft werden, ob die Eigenschaft Attributes.Length > 0. Falls ja, kann auf jedes Attribut zugegriffen werden. Auch hier kann wieder mit einer Objektvariablen vom Typ „IXMLDOMAttribute“ gearbeitet werden: Dim x0 As New MSXML.DOMDocument Dim x1 As MSXML.IXMLDOMElement Dim x2 As MSXML.IXMLDOMAttribute Dim strAusgabe As String x0.Load "c:\Eigene Dateien\XML\Kapitel06\test13.xml" Set x1 = x0.documentElement If x1.Attributes.Length > 0 Then For Each x2 In x1.Attributes strAusgabe = strAusgabe & vbCr & _ x2.baseName & ": " & x2.Text Next End If MsgBox strAusgabe
Abbildung 6.24: Das Attribut (alle Attribute) mit Name und Inhalt
Das Objekt DOMDocument besitzt einige „Create“-Methoden. Mit der korrekten Variablendeklaration kann an eine Objektvariable das entsprechende Objekt übergeben werden. Folgende Typen stehen hierbei zur Verfügung: Dim Dim Dim Dim Dim Dim Dim Dim
x0 x1 x2 x3 x4 x5 x6 x7
As As As As As As As As
New MSXML.DOMDocument MSXML.IXMLDOMAttribute MSXML.IXMLDOMCDATASection MSXML.IXMLDOMComment MSXML.IXMLDOMDocumentFragment MSXML.IXMLDOMElement MSXML.IXMLDOMEntityReference MSXML.IXMLDOMNode
:HLWHUH ;0/(OHPHQWH LQ 9%$
229
Sandini Bib Dim x8 As MSXML.IXMLDOMProcessingInstruction Dim x9 As MSXML.IXMLDOMText
Sie werden gesetzt: Set Set Set Set Set Set Set Set Set
Alle oben aufgelisteten Methoden verlangen eine Zeichenkette. Lediglich „createNode“ verlangt die Parameter „type, name as String, namespaceURI as String“. „CreateProcessingInstruction“ benötigt ebenfalls mehr als einen Übergabewert: „target as String, data as String“. Das Hinzufügen von neuen Knoten umfasst immer zwei Phasen: Zuerst muss ein Knoten erzeugt werden. Dann wird der Knoten an das korrekte Elternelement angehängt. Bei den meisten create-Methoden funktioniert es auf die gleiche Weise, lediglich Attribute werden etwas anders angehängt. Das folgende Beispiel illustriert das Vorgehen: Sub NeueElemente() Dim xml As New MSXML.DOMDocument Dim Dim Dim Dim Dim Dim Dim
Dim x8 As MSXML.IXMLDOMDocumentFragment Dim x9 As MSXML.IXMLDOMEntityReference xml.loadXML ""
Ein leeres Dokument wurde erzeugt. Im ersten Schritt wird die Processing Instruction eingefügt. Beachten Sie, dass der Parameter target „xml“ sein muss. Er liegt lediglich in der Version 1.0 vor.
Anschließend wird ein Kommentar erzeugt und eingefügt. Set x2 = xml.createComment _ ("Dieses Beispiel dient lediglich dazu, " & _ "die create-Methoden zu zeigen.") xml.appendChild x2
Danach wird das Wurzelelement erzeugt und in das XML-Dokument eingefügt. Set x3 = xml.createElement("Wurzel") xml.appendChild x3
Ein Attribut wird erzeugt. Es besteht aus Attributname („Eigenschaft“) und Wert („feucht, lang, dunkel und braun“). Beachten Sie, dass das Attribut nicht mit der Methode appendChild hinzugefügt wird, sondern mit der Methode setNamedItem an die Sammlung „Attributes“ angehängt wird. Set x4 = xml.createAttribute("Eigenschaft") x4.Text = "feucht, lang, dunkel und braun" x3.Attributes.setNamedItem x4
Übrigens: Man kann ein Attribut leichter mit der Methode setAttribute anhängen. Diese Methode verlangt zwei Parameter: name und value: x3.setAttribute "Eigenschaft2", "unter der Erde"
Schließlich wird ein Knoten erzeugt und an das Wurzelelement angefügt. Set x5 = xml.createNode(NODE_ELEMENT, "Knoten", "") x3.appendChild x5
Danach wird ein CDATA-Abschnitt erzeugt, der Zeichendaten enthält, die nicht geparst werden („>>“). Dieser Abschnitt wird an den Knoten angehängt. Set x6 = xml.createCDATASection(">>") x5.appendChild x6
Dem Knoten wird ein Inhalt zugewiesen: Set x7 = xml.createTextNode("und ich bin der Inhalt") x5.appendChild x7
Knoten können auch auf andere Weise erzeugt werden. Der Textinhalt kann als Eigenschaft des Knotens gesehen werden:
:HLWHUH ;0/(OHPHQWH LQ 9%$
231
Sandini Bib Set x5 = xml.createNode(NODE_ELEMENT, "Knoten", "") x5.Text = "ich bin der zweite Inhalt" x3.appendChild x5
Oder als Element: Set x3x = xml.createElement("Knoten") x3x.Text = "ich bin der dritte Knoten" x3.appendChild x3x
Wer es lieber kurz und bündig (englisch: „quick and dirty“) haben möchte, der kann es auch wie folgt schreiben: x3.appendChild(xml.createElement("Knoten")).Text = _ "ich bin der vierte Knoten"
Die vorhergehenden Schreibweisen sind sicherlich übersichtlicher. Und: Vergessen Sie nicht, das gesamte Dokument zu speichern! xml.Save "c:\Eigene Dateien\XML\Kapitel06\test17.xml" End Sub
Abbildung 6.25: Das Ergebnis des XML-Dokuments, das mit VBA erzeugt wurde
6.4.3 Eine Entity-Referenz erzeugen Ein Problem stellt die Entity-Referenz dar. Zwar kann in das Dokument eine Entity-Referenz der Gestalt: xml.createEntityReference("U")
232
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
eingefügt werden, aber es nicht möglich, per Programmierung eine interne DTD zu erstellen, in der die Entität „U“ aufgelöst wird. Die Eigenschaft doctype des Dokuments ist schreibgeschützt, das heißt sie kann zwar gelesen, aber nicht geschrieben werden. Deshalb macht das Setzen einer neuen Entity-Referenz nur Sinn, wenn sie schon definiert wurde. Angenommen ein Dokument hat folgende Gestalt: ]> 12 Loriot's Wum & Wendelin 13 Loriot's Heile Welt
Dann kann man es öffnen, ein neues Element anhängen und speichern: Sub NeueElemente2() Dim xml As New MSXML.DOMDocument Dim x2 As MSXML.IXMLDOMElement Dim x3 As MSXML.IXMLDOMElement Dim x9 As MSXML.IXMLDOMEntityReference xml.Load "c:\Eigene Dateien\XML\Kapitel05\bsp13.xml" Set x9 = xml.createEntityReference("U") Set x2 = xml.createElement("Buch") xml.documentElement.appendChild x2 Set x3 = xml.createElement("Nummer") x3.Text = "14"
:HLWHUH ;0/(OHPHQWH LQ 9%$
233
Sandini Bib x2.appendChild x3 Set x3 = xml.createElement("Titel") x3.Text = "Loriot's großer Ratgeber" x2.appendChild x3 Set x3 = xml.createElement("Gruppe") x3.appendChild x9 x2.appendChild x3 xml.Save "c:\Eigene Dateien\XML\Kapitel06\test18.xml" End Sub
Abbildung 6.26: Ein neues Element mit Entity-Referenz wird eingefügt
6.4.4 Löschen von Elementen im XML-Dokument Elemente können neu erzeugt, aber auch gelöscht werden: x1.removeAttribute "Att1" x1.removeAttributeNode x2 x1.removeChild x3
Beachten Sie, dass das Dokument danach wieder gespeichert werden muss!
234
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
6.4.5 Fehler in einem wohlgeformten XML-Dokument Befindet sich im XML-Dokument ein Fehler, so kann dieser abgefangen, überprüft und darauf reagiert werden. Wurde das Dokument nach dem Laden erfolgreich geparst, dann ist die Eigenschaft „parsed“ „wahr“. Für Fehler nach dem Parsen steht das Objekt „IXMLDOMParseError“ zur Verfügung. Es besitzt eine Reihe von Eigenschaften: Dim x0 As New MSXML.DOMDocument Dim xmlFehler As MSXML.IXMLDOMParseError Dim strAusgabe As String x0.Load "c:\Eigene Dateien\XML\Kapitel06\fehler01.xml" If x0.parsed = True Then Set xmlFehler = x0.parseError With xmlFehler strAusgabe = "Fehlernummer: " & vbTab & .errorCode & vbCr strAusgabe = strAusgabe & "Fehlerposition" & _ vbTab & .filepos & vbCr strAusgabe = strAusgabe & "Zeile:" & vbTab & vbTab & _ .Line & vbCr strAusgabe = strAusgabe & "Zeilenposition: " & vbTab & _ .linepos & vbCr strAusgabe = strAusgabe & "Fehlergrund: " & vbTab & .reason & vbCr strAusgabe = strAusgabe & "Fehlertext" & vbTab & _ vbTab & .srcText & vbCr strAusgabe = strAusgabe & "Fehlerdatei: " & _ vbTab & .URL End With MsgBox strAusgabe End If
Abbildung 6.27: Eine fehlerhafte Datei
:HLWHUH ;0/(OHPHQWH LQ 9%$
235
Sandini Bib
Damit können natürlich auch mehrere Dateien auf Fehler überprüft werden: Dim Dim Dim Dim
x0 As New MSXML.DOMDocument xmlFehler As MSXML.IXMLDOMParseError wdApp As Word.Application wdDoc As Word.Document
Dim strDateiname As String Dim intZähler As Integer Set wdApp = Word.Application Set wdDoc = wdApp.Documents.Add For intZähler = 1 To 8 strDateiname = "c:\Eigene Dateien\XML\Kapitel06\fehler" & _ Format(intZähler, "00") & ".xml" x0.Load strDateiname If x0.parsed = True Then Set xmlFehler = x0.parseError wdApp.Selection.ParagraphFormat _ .LeftIndent = CentimetersToPoints(4) wdApp.Selection.ParagraphFormat _ .FirstLineIndent = CentimetersToPoints(-4) With xmlFehler wdApp.Selection.TypeText _ "Fehlernummer: " & vbTab & .errorCode & vbCr & _ "Fehlerposition" & vbTab & .filepos & vbCr & _ "Zeile:" & vbTab & .Line & vbCr & _ "Zeilenposition: " & vbTab & .linepos & vbCr & _ "Fehlergrund: " & vbTab & .reason & _ "Fehlertext" & vbTab & .srcText & vbCr & _ "Fehlerdatei: " & vbTab & .URL & vbCr & vbCr End With End If Next
236
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
Das Ergebnis wird in eine neue, leere Worddatei geschrieben. Es sieht wie folgt aus:
Abbildung 6.28: Die Liste der fehlerhaften Dateien
Natürlich kann man überprüfen, ob kein Fehler vorgefallen ist: [...] If xmlFehler.filepos = 0 Then wdApp.Selection.ParagraphFormat _ .LeftIndent = CentimetersToPoints(0) wdApp.Selection.ParagraphFormat _ .FirstLineIndent = CentimetersToPoints(0) wdApp.Selection.TypeText _ "In der Datei " & x0.URL & _ " wurde kein Fehler gefunden." & vbCr & vbCr Else [...]
:HLWHUH ;0/(OHPHQWH LQ 9%$
237
Sandini Bib
Abbildung 6.29: Nun werden auch die XML-Dokumente oder Fehler angezeigt
Übrigens: Es wurden die fehlerhaften Dateien verwendet, deren Fehler in den Übungen in Kapitel 1 zu suchen waren.
6.4.6 Fehler in einem gültigen XML-Dokument Bisher wurden wohlgeformte Dokumente getestet. Ist das Dokument sogar gültig, das heißt, besitzt es eine DTD, Namensräume oder andere Objekte, dann kann eine Validierung erfolgen, wenn man die Eigenschaft ValidateOnParse auf „True“ setzt. Während des Parsens (ValidateOnParse = True) schlägt die Validierung fehl, wenn resolveExternals nicht auf „True“ gesetzt wurde. Der Internet Explorer von Microsoft validiert beispielsweise nicht die DTD eines XML-Dokuments. Ein Beispiel: Gegeben sei folgendes XML-Dokument: ]> Fips, der Affe
238
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
In der DTD hat sich ein Fehler eingeschlichen: Dort wird das Element „Tytel“ festgelegt, welches allerdings „Titel“ im Dokument heißt. Werden die beiden Eigenschaften resolveExternals und validateOnParse auf False gesetzt, dann wird das Dokument korrekt durchlaufen: Sub DTDPrüfung() Dim x0 As New MSXML.DOMDocument Dim xmlKnoten As MSXML.IXMLDOMNode ... Dim strAusgabe As String x0.async = False x0.resolveExternals = False x0.validateOnParse = False x0.Load "c:\Eigene Dateien\XML\Kapitel06\fehler10.xml" MsgBox "Die DTD lautet: " & vbCr & _ x0.xml For Each xmlKnoten In x0.childNodes Select Case xmlKnoten.nodeType
Werden dagegen beide Eigenschaften auf „True“ gesetzt, dann erkennt der Parser, dass das Dokument nicht gültig ist, und zeigt kein Element an. Dafür kann der Grund im ParseError-Objekt ausgegeben werden: x0.async = False x0.resolveExternals = True x0.validateOnParse = True x0.Load "c:\Eigene Dateien\XML\Kapitel06\fehler10.xml" MsgBox "Die DTD lautet: " & vbCr & _ x0.xml MsgBox x0.parseError.reason
Damit wird Folgendes klar: Per VBA können existierende Daten in eine XML-Datei geschrieben werden. Dabei können problemlos Elemente, Attribute und Inhalte erzeugt werden. Umgekehrt kann ein XML-Dokument per VBA durchlaufen und die darin befindlichen Daten können in eine Excel-Tabelle (oder eine Datenbank) geschrieben werden. Man kann wohlgeformte Dokumente auf ihre Gültigkeit überprüfen und im Falle eines Fehlers diesen bestimmen lassen. Leider ist es nicht möglich, eine DTD zu erzeugen. Allerdings kann eine gültige Datei mit einer (internen oder externen DTD) validiert werden. So kann sichergestellt werden, dass nachträgliche Veränderungen am Dokument noch immer der Dokumentdefinition entsprechen.
:HLWHUH ;0/(OHPHQWH LQ 9%$
239
Sandini Bib
6.4.7 Das Lokal-Fenster Einen Überblick über die Objekte, Methoden und Eigenschaften kann man sich leicht im Objektkatalog verschaffen. Wählen Sie hierzu die richtige Bibliothek aus, und Sie erhalten eine Auflistung der Elemente:
Abbildung 6.30: Der Objektkatalog gibt Aufschluss über die vorhandenen Objekte
Neben dem Objektkatalog kann man sich in VBA (und VB) leicht einen Überblick über die einzelnen Eigenschaften und Methoden eines Objekts verschaffen, indem man das Lokal-Fenster (Menü ANSICHT) öffnet, wenn das Programm im Einzelschrittmodus läuft. Dort finden sich zu einer laufenden Prozedur die Objekte, ihre Eigenschaften und die aktuellen Werte.
240
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
Abbildung 6.31: Das Lokalfenster
6.5
Von Access mit VBA nach XML und zurück
Mit dem bisherigen Wissen ist es nun nicht mehr schwierig, Daten aus Access in eine XML-Datei zu schreiben. Das dahinter liegende Prinzip sieht folgendermaßen aus: Sie greifen auf eine Tabelle (oder Abfrage) innerhalb einer Datenbank zu, durchlaufen alle Datensätze und fügen sie in ein (neues) XML-Dokument ein. Das folgende Beispiel öffnet die Tabelle „tab_Mitglieder“, legt ein neues XML-Dokument mit dem Wurzelelement an und erzeugt für jeden Datensatz einen neuen Knoten , in dem sich die beiden Elemente und befinden. Ihnen wird der Text zugewiesen, der aus der Datenbank ausgelesen wurde: Sub Export() Dim xml As New MSXML.DOMDocument Dim xmlKD As MSXML.IXMLDOMElement Dim xmlUnterKD1 As MSXML.IXMLDOMElement Dim xmlUnterKD2 As MSXML.IXMLDOMElement Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection
9RQ $FFHVV PLW 9%$ QDFK ;0/ XQG ]XUFN
241
Sandini Bib rs.CursorType = adOpenStatic rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" xml.loadXML "" Do Set xmlKD = xml.createElement("Kunde") Set xmlUnterKD1 = xml.createElement("Vorname") Set xmlUnterKD2 = xml.createElement("Zuname") xmlKD.appendChild(xmlUnterKD1).Text = _ rs.Fields("tMitZuname").Value xmlKD.appendChild(xmlUnterKD2).Text = _ rs.Fields("tMitVorname").Value xml.documentElement.appendChild xmlKD rs.MoveNext Loop While Not rs.EOF xml.Save "c:\Eigene Dateien\XML\Kapitel06\test14.xml" rs.Close Set rs = Nothing End Sub
Man kann ebenso Attribute, die aus der Tabelle gewonnen werden, in das Dokument einfügen: ... Do Set xmlKD = xml.createElement("Kunde") Set xmlUnterKD1 = xml.createElement("Vorname") Set xmlUnterKD2 = xml.createElement("Zuname") xmlKD.appendChild(xmlUnterKD1).Text = _ rs.Fields("tMitZuname").Value xmlKD.appendChild(xmlUnterKD2).Text = _ rs.Fields("tMitVorname").Value xmlKD.setAttribute "Kundennr", _ rs.Fields("tMitID").Value xml.documentElement.appendChild xmlKD rs.MoveNext Loop While Not rs.EOF xml.Save "c:\Eigene Dateien\XML\Kapitel06\test15.xml" ...
242
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
Abbildung 6.32: Die exportierte Namensliste
Und der umgekehrte Fall funktioniert analog. Ein XML-Dokument, dessen Struktur bekannt ist, wird durchlaufen, die Texte der Elemente werden in einer Datenbank in eine Tabelle geschrieben. Zuerst wird das XML-Dokument geladen und sein Wurzelelement ermittelt. Der Name des Wurzelelements wird an eine Variable übergeben: Sub Import() Dim xml As New MSXML.DOMDocument Dim strName As String Dim i As Integer Dim rs As ADODB.Recordset Dim cmd As ADODB.Command Dim cat As New ADOX.Catalog xml.Load "c:\Eigene Dateien\XML\Kapitel06\test15.xml" strName = xml.documentElement.baseName
Anschließend wird überprüft, ob schon eine gleichlautende Tabelle vorliegt. Falls eine Schleife eine solche findet, dann wird sie gelöscht: Set rs = New ADODB.Recordset Set cat.ActiveConnection = CurrentProject.Connection For i = cat.Tables.Count - 1 To 0 Step -1 If cat.Tables(i).Name = strName Then
9RQ $FFHVV PLW 9%$ QDFK ;0/ XQG ]XUFN
243
Sandini Bib Set cmd = New ADODB.Command cmd.CommandText = "DROP TABLE " & strName cmd.ActiveConnection = CurrentProject.Connection Set rs = cmd.Execute End If Next
Beachten Sie, dass die Schleife rückwärts zählt. Angenommen, es gäbe zehn Tabellen, angenommen, die fünfte wäre die gefundene, dann würde diese gelöscht werden. Nun wird allerdings keine zehnte mehr gefunden: cat.Tables(i).Name würde zu einem Fehler führen. Nach dem Löschvorgang wird eine Tabelle erzeugt, die den Namen des Wurzelelements des XML-Dokuments hat: Set cmd = New ADODB.Command cmd.CommandText = "CREATE TABLE " & strName & _ "(Nr long, Vorname string, Zuname string)" cmd.ActiveConnection = CurrentProject.Connection Set rs = cmd.Execute
Danach wird auf die Tabelle („rs“) zugegriffen: rs.CursorType = adOpenKeyset rs.LockType = adLockOptimistic rs.Open "SELECT * FROM " & strName
Eine Schleife durchläuft alle Kindknoten und trägt die Inhalte in die Felder der Tabelle ein: For i = 0 To xml.documentElement.childNodes.length - 1 rs.AddNew rs.Fields("Nr").Value = _ CLng(xml.documentElement.childNodes(i). _ Attributes(0).Text) rs.Fields("Vorname").Value = _ xml.documentElement.childNodes(i). _ childNodes(0).Text rs.Fields("Zuname").Value = _ xml.documentElement.childNodes(i). _ childNodes(1).Text rs.Update Next i
Ein sauberer Programmierstil verlangt nicht nur das Schließen des Recordsets, sondern auch das Zerstören der Objektvariablen: rs.Close Set cat = Nothing
244
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib Set cmd = Nothing Set rs = Nothing Set xml = Nothing End Sub
Abbildung 6.33: Die re-importierten Daten
6.6
Das TreeView-Steuerelement von VB
Wenn Sie die Developer-Edition von Office installiert haben oder sich auf Ihrem Rechner die Programmiersprache Visual Basic befindet, dann können Sie zu den Steuerelementen der Dialoge über die rechte Maustaste das TreeView-Steuerelement hinzuladen. Es besitzt folgende wichtige Eigenschaften: Eigenschaft LineStyle
Beschreibung 0 - tvwTreeLines oder 1 – tvwRootLines Bestimmt die Art der angesetzten Linie. Bei tvwRootLines erscheint ein +-Symbol zum Öffnen der Liste
LabelEdit
0 – tvwLabelAutomatic oder 1 – tvwLabelManual Bestimmt, ob die Beschriftung mit einem zweiten Klick auf einen Knoten geändert werden kann (tvwLabelAutomatic).
Indentation
Einrückung der einzelnen Ebenen
Tabelle 6.6: Die wichtigsten Eigenschaften des TreeView-Steuerelements
'DV 7UHH9LHZ6WHXHUHOHPHQW YRQ 9%
245
Sandini Bib Eigenschaft
Beschreibung
Sorted
legt fest, ob die Knoten der obersten Ebene sortiert sind.
Style
0 – tvwTextOnly 1 – tvwPictureText 2 – tvwPlusMinusText 3 – tvwPlusPictureText 4 – tvwTreelinesText 5 – tvwTreelinesPictureText 6 – tvwTreelinesPlusMinusText 7 – tvwTreelinesPlusMinusPictureText die verschiedenen Darstellungsmöglichkeiten der Liste
PathSeparator
das Trennzeichen, das für die Path-Eigenschaft verwendet wird
Appearance
0 – ccFlat 1 – cc3D das Aussehen des TreeView-Steuerelements
Tabelle 6.6: Die wichtigsten Eigenschaften des TreeView-Steuerelements (Forts.)
Per VBA stehen Ihnen für das Steuerelement weitere Eigenschaften zur Verfügung: Eigenschaft
Beschreibung
Nodes
die Nodes-Sammlung
SelectedItem
der zurzeit ausgewählte Knoten
Tabelle 6.7: Die wichtigsten Eigenschaften des TreeView-Steuerelements in VBA
Das Objekt „SelectedItem“ stellt einen Knoten dar, der wiederum einige Eigenschaften und Methoden besitzt: Eigenschaft
Beschreibung
Text
der angezeigte Text des Knotens
Key
die Zeichenkette, mit der auf den Knoten zugegriffen werden kann
FullPath
der gesamte Pfad des Knotens
Expanded
gibt an, ob der Knoten zurzeit geöffnet („True“) ist oder nicht
Selected
gibt an, ob der Knoten markiert ist („True“)
Sorted
legt fest, dass die Unterknoten sortiert werden
Parent
der Elternknoten
Child
der erste Kindknoten
FirstSibling
der erste Geschwisterknoten
Tabelle 6.8: Die wichtigsten Eigenschaften eines Knotens (nur in VBA verfügbar)
246
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib Eigenschaft
Beschreibung
LastSibling
der letzte Geschwisterknoten
Next
der nächste Geschwisterknoten
Previous
der vorangehende Geschwisterknoten
Children
die Anzahl der Kindknoten
ExpandedImage
enthält den Index eines Bitmaps, der für den Knoten im geöffneten Zustand verwendet wird
SelectedImage
der Index eines Bitmaps, der für den Knoten im selektierten Zustand verwendet wird
Tabelle 6.8: Die wichtigsten Eigenschaften eines Knotens (nur in VBA verfügbar) (Forts.)
Und neben den Eigenschaften existieren noch einige (wenige) Methoden des TreeView-Steuerelements. Hier eine kleine Auswahl: Methode
Beschreibung
GetVisibleCount
die Anzahl der sichtbaren Knoten
HitTest
die x- und y-Koordinaten des Node-Objekts
StartLabelEdit
schaltet bei LabelEdit = tvwManual den Namen des Knotens in den Bearbeitungsmodus
Refresh
aktualisiert das Steuerelement
Tabelle 6.9: Die wichtigsten Methoden des TreeView
Während ein einzelner Knoten über keine bedeutende Methode verfügt, besitzt die Sammlung Nodes die drei wichtigen Methoden Add, Clear und Remove. Mit ihrer Eigenschaft Count kann die Anzahl bestimmt werden. Das Steuerelement verfügt weiter über die Ereignisse BeforeLabelEdit und AfterLabelEdit, Click und NodeClick (wenn ein Knoten angeklickt wird), Expand und Collapse (wenn ein Knoten geöffnet und geschlossen wird). Damit ist nun das Fundament gelegt, um ein solches Steuerelement zu füllen. Auf einer Userform werden mehrere Schaltflächen angebracht. Die erste soll das Steuerelement (tvwListe) lediglich mit einem Text füllen: Dim i As Integer Dim j As Integer Dim tvwKnoten As Node With tvwListe .LineStyle = tvwRootLines .Style = tvwTreelinesPlusMinusPictureText .Indentation = 0 End With
'DV 7UHH9LHZ6WHXHUHOHPHQW YRQ 9%
247
Sandini Bib For i = 1 To 3 Set tvwKnoten = Me.tvwListe.Nodes.Add _ (Key:="Kapitel" & i, Text:="Kapitel" & i) For j = 1 To 5 Me.tvwListe.Nodes.Add Relative:=tvwKnoten, _ Relationship:=tvwChild, Key:="Kapitel" & i & j, _ Text:="Unterkapitel" & j Next j Next i
Abbildung 6.34: Im Treeview-Steuerelement werden drei Knoten mit jeweils fünf Kindknoten erzeugt
Zu Beginn werden einige der Eigenschaften des Steuerelements festgelegt. Danach zählen zwei Zählervariablen (i und j) von 1 bis 3 beziehungsweise 5 hoch. Die Methode Add erzeugt einen neuen Knoten. Ihm kann optional ein Key zugewiesen werden, über den man per Programmierung auf diesen Schlüssel zugreifen kann. Der Text wird angezeigt. Wird der Parameter Relative auf einen Knoten gesetzt und die Eigenschaft Relationship auf tvwChild (eine interne Konstante) gesetzt, dann wird an diesen Knoten ein Kindknoten angefügt. Wollte man mit diesen Knoten weiterarbeiten, könnte man deklarieren: Dim tvwUnterknoten As Node
und schließlich die Add-Methode modifizieren:
248
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib Set tvwUnterknoten = Me.tvwListe.Nodes.Add _ (Relative:=tvwKnoten, Relationship:=tvwChild, _ Key:="Kapitel" & i & j, Text:="Unterkapitel" & j)
Und damit kann auf ein XML-Dokument zugegriffen werden. Angenommen, das Dokument hat folgende Gestalt: Die Unheimlichkeit des Blicks Von Caligari zu Hitler Die dämonische Leinwand Mr. Hitchcock, wie haben Sie das gemacht? Peter Lorre Patty Difusa y otros textos
Und nun kann einer Schaltfläche folgender Code zugewiesen werden: Dim Dim Dim Dim
xml As New MSXML.DOMDocument x2 As MSXML.IXMLDOMElement tvw As Node tvwKind As Node
xml.async = True xml.Load "c:\Eigene Dateien\XML\Kapitel06\test16.xml" With Me.tvwListe .LineStyle = tvwRootLines .Style = tvwTreelinesPlusMinusPictureText .Indentation = 0 Set tvw = .Nodes.Add(, , , "Artikelname") For Each x2 In x1.documentElement.childNodes Set tvwKind = .Nodes.Add(1, tvwChild) tvwKind.Text = x2.nodeTypedValue Next End With
'DV 7UHH9LHZ6WHXHUHOHPHQW YRQ 9%
249
Sandini Bib
Abbildung 6.35: Die Daten werden aus einer XML-Datei geholt
Das Dokument wird geladen, der Wurzelknoten wird durchlaufen und die Inhalte der Knoten werden angezeigt. Damit das Beispiel funktioniert, das heißt, damit die alte, bereits angezeigte Liste entfernt wird, muss sie mit folgender Methode gelöscht werden: Me.tvwListe.Nodes.Clear
Das Ganze kann man auf mehreren Ebenen durchlaufen lassen: Private Sub cmd03_Click() Dim xml As New MSXML.DOMDocument Dim x1 As MSXML.IXMLDOMElement Dim x2 As MSXML.IXMLDOMElement Dim tvw As Node Dim tvwKind1 As Node Dim tvwKind2 As Node Dim tvwKind3 As Node xml.async = True xml.Load "c:\Eigene Dateien\XML\Kapitel06\test13.xml" With Me.tvwListe .Nodes.Clear .LineStyle = tvwRootLines .Style = tvwTreelinesPlusMinusPictureText
250
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib .Indentation = 0 Set tvw = .Nodes.Add(, , , xml.documentElement.baseName) For Each x1 In xml.documentElement.childNodes Set tvwKind1 = .Nodes.Add(1, tvwChild) tvwKind1.Text = x1.baseName For Each x2 In x1.childNodes Set tvwKind2 = .Nodes.Add(tvwKind1, tvwChild) tvwKind2.Text = x2.baseName Set tvwKind3 = .Nodes.Add(tvwKind2, tvwChild) tvwKind3.Text = x2.Text Next Next End With End Sub
Abbildung 6.36: Mehrere Ebenen werden angezeigt
Oder auch rekursiv: Private Sub cmd03_Click() Dim xml As New MSXML.DOMDocument Dim x1 As MSXML.IXMLDOMElement Dim tvw As Node xml.async = True
'DV 7UHH9LHZ6WHXHUHOHPHQW YRQ 9%
251
Sandini Bib xml.Load "c:\Eigene Dateien\XML\Kapitel06\test13.xml" With Me.tvwListe .Nodes.Clear .LineStyle = tvwRootLines .Style = tvwTreelinesPlusMinusPictureText .Indentation = 0 Set tvw = .Nodes.Add(, , , _ xml.documentElement.baseName) For Each x1 In xml.documentElement.childNodes Call KnotenAnzeigen(x1, tvw) Next End With End Sub Sub KnotenAnzeigen(x0 As MSXML.IXMLDOMElement, _ tvwKind As Node) Dim tvwKind1 As Node Dim tvwKind2 As Node Dim x1 As MSXML.IXMLDOMElement Set tvwKind1 = Me.tvwListe.Nodes.Add(tvwKind, tvwChild) tvwKind1.Text = x0.baseName If x0.childNodes.Length > 1 Then For Each x1 In x0.childNodes Call KnotenAnzeigen(x1, tvwKind1) Next Else Set tvwKind2 = Me.tvwListe.Nodes.Add _ (tvwKind1, tvwChild) tvwKind2.Text = x0.Text End If End Sub
Die Prozedur „KnotenAnzeigen“ wird immer dann aufgerufen, wenn ein Element mehr als einen Unterknoten hat. Besitzt er genau einen, so die Voraussetzung, dann ist dieser Knoten ein Textknoten, also Inhalt. Dann wird er angezeigt. Besitzt er mehr als einen, so werden sie als Knoten interpretiert, welche weitere Kindknoten besitzen dürfen. Dieses Beispiel geht davon aus, dass ein Elternknoten mehr als einen Kindknoten besitzt, dass ein Knoten keine gemischten Inhalte beinhaltet und dass keine leeren Elemente vorhanden sind.
252
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
Und mit diesen Befehlen ist es nun leicht möglich, eine XML-Datei dynamisch zu halten. Die vierte Schaltfläche („cmd04“) oder das Ereignis „UserForm_Initialize“ füllt das Steuerelement. Das Markieren eines beliebigen Eintrags zeigt die dazu gespeicherten Daten in mehreren Textfeldern an. Im folgenden Beispiel besitzt das Wurzelelement „Autorenliste“ vier Kindelemente, die jeweils unterschiedliche Namen tragen: Hofstadter Douglas R. Gardner Martin Ifrah Georges Hawkins Steve
Beim Laden werden lediglich die Namen der Knoten angezeigt: ... Set tvw = .Nodes.Add(, , , xml4.documentElement.baseName) For Each x1 In xml4.documentElement.childNodes Set tvwKind1 = .Nodes.Add(1, tvwChild) tvwKind1.Text = x1.baseName For Each x2 In x1.childNodes Set tvwKind2 = .Nodes.Add(tvwKind1, tvwChild) tvwKind2.Text = x2.baseName Next Next End With
'DV 7UHH9LHZ6WHXHUHOHPHQW YRQ 9%
253
Sandini Bib
Abbildung 6.37: Das XML-Dokument
In den beiden Textfeldern „txtVorname“ und „txtZuname“ werden die Inhalte angezeigt, wenn der Benutzer auf den entsprechenden Eintrag klickt. Dazu wird das Ereignis „Click“ des Strukturansicht-Steuerelements (TreeView) verwendet. Beachten Sie, dass xml4 als Dokument global deklariert wurde. Ärgerlich ist, dass die Eigenschaft SelectedItem.FullPath des Steuerelements den Pfad mit „\“ angibt (also beispielsweise „Autorenliste\Autor01\Vorname“, während die Eigenschaft „getElementsByTagName“ einen „/“ verlangt. Seit VBA 6.0 steht hierfür der Befehl Replace zur Verfügung. Damit es nicht zu einer Zeichenkette wie „Autorenliste\Autor01\Vorname\Vorname“ kommen kann, wird die Zeichenkette auf jeweils 20 Zeichen von links mit der Funktion Left beschnitten. Private Sub tvwListe_Click() ... strListe = Me.tvwListe.SelectedItem.FullPath If InStr(1, strListe, "\") > 0 Then strListe = Replace(strListe, "\", "/") strListe = Left(strListe, 20) Me.txtZuname.Value = _ xml4.getElementsByTagName(strListe & _ "/" & "Zuname")(0).Text
254
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib Me.txtVorname.Value = _ xml4.getElementsByTagName(strListe & _ "/" & "Vorname")(0).Text End If End Sub
Abbildung 6.38: Die Daten werden in Textfeldern angezeigt
Hinter der Ändern-Schaltfläche verbirgt sich folgender Code: Private Sub cmdÄndern_Click() If strListe "" Then xml4.getElementsByTagName(strListe & _ "/" & "Zuname")(0).Text = _ Me.txtZuname.Value xml4.getElementsByTagName(strListe & _ "/" & "Vorname")(0).Text = _ Me.txtVorname.Value xml4.Save xml4.URL End If End Sub
Auch die Variable strListe wurde modulweit deklariert. Damit wird sichergestellt, dass der Benutzer bereits einen Eintrag aus der Liste ausgewählt hat. An dieses Element werden die (neuen) Inhalte der beiden Textfelder übergeben.
'DV 7UHH9LHZ6WHXHUHOHPHQW YRQ 9%
255
Sandini Bib
Das Leeren der Textbox bereitet sicherlich keine Schwierigkeiten: Private Sub cmdLeeren_Click() Me.txtVorname.Value = "" Me.txtZuname.Value = "" Me.txtVorname.SetFocus End Sub
Und auch das Anlegen von neuen Datensätzen ist kein Aufwand. Zuerst wird überprüft, ob der Benutzer etwas eingegeben hat. Anschließend wird getestet, ob schon mehr als 99 Datensätze vorliegen (dies hängt mit der Schreibweise „Autor01“ zusammen): Private Sub cmdNeu_Click() Dim x1 As MSXML.IXMLDOMElement Dim x2 As MSXML.IXMLDOMElement Dim intAnzahl As Integer If Me.txtVorname.Value = "" Then MsgBox "Bitte einen Vornamen eingeben!" Me.txtVorname.SetFocus ElseIf Me.txtZuname.Value = "" Then MsgBox "Bitte einen Zunamen eingeben!" Me.txtZuname.SetFocus Else intAnzahl = xml4.documentElement.childNodes.Length + 1 If intAnzahl > 99 Then MsgBox "Die Obergrenze ist erreicht." Exit Sub End If Set x1 = xml4.createElement("Autor" & _ Format(intAnzahl, "00")) xml4.documentElement.appendChild x1 Set x2 = xml4.createElement("Zuname") x2.Text = Me.txtZuname.Value x1.appendChild x2 Set x2 = xml4.createElement("Vorname") x2.Text = Me.txtVorname.Value x1.appendChild x2 xml4.Save xml4.URL Call cmd04_Click End If End Sub
Das eigentliche Programm erzeugt ein neues Element („x1“) „Autorxx“ und hängt es an. Zwei weitere Elemente „Vorname“ und „Zuname“ wer-
256
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
den geschaffen, ihnen wird der Text zugewiesen, der sich in den beiden Textfeldern befindet. Sie werden an das erste Element x1 angehängt. Danach wird das Dokument gespeichert. Damit der Benutzer die aktuelle Ansicht sieht, wird die vierte Befehlsschaltfläche „cmd04_Click“ aufgerufen. Damit wird die Liste geleert und mit den aktuellen Daten erneut gefüllt.
Abbildung 6.39: Neue Daten werden eingegeben ...
Abbildung 6.40: ... und damit wird ein neuer Datensatz angelegt
'DV 7UHH9LHZ6WHXHUHOHPHQW YRQ 9%
257
Sandini Bib
6.7
Zusammenfassung
In diesem Kapitel wurde gezeigt, wie per VBA-Programmierung eine Excel- oder eine Access-Tabelle durchlaufen wird, und wie die Daten in ein wohlgeformtes XML-Dokument geschrieben werden. Außerdem wurde der umgekehrte Weg beschrieben: Eine XML-Datei wird geöffnet und die Elemente werden in eine Datenbank oder Tabelle eingetragen. Schließlich wurde gezeigt, wie mit Hilfe von VBA ein Dokument auf Gültigkeit überprüft werden kann. Und wie Fehler in einem Dokument lokalisiert werden können. Zum Schluss wurde das TreeView-Steuerelement vorgestellt. Es stellt nicht nur eine elegante Möglichkeit dar, XML-Daten baumartig darzustellen, mit seiner Hilfe kann der Benutzer vorhandene Daten ändern und neue Elemente anhängen.
6.8
Fragen zu Kapitel 6
Frage 1 Wie sieht das XML-Dokument aus, das durch folgenden VBA-Code erzeugt wird? Sub Uebung01() Dim xml As New MSXML.DOMDocument Dim Dim Dim Dim Dim
Set x2 = xml.createComment("Dieses Beispiel dient lediglich dazu, " & _ "die sieben Zwerge zu zeigen.") xml.appendChild x2
258
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib Set x3 = xml.createElement("Snow_White") xml.appendChild x3 Set x4 = xml.createAttribute("Dwarfs") x4.Text = "sieben" x3.Attributes.setNamedItem x4 Set x5 = xml.createElement("Zwerg") x5.Text = "Dopey" x3.appendChild x5 Set x5 = xml.createElement("Zwerg") x5.Text = "Sleepy" x3.appendChild x5 Set x5 = xml.createElement("Zwerg") x5.Text = "Doc" x3.appendChild x5 Set x5 = xml.createElement("Zwerg") x5.Text = "Sneezy" x3.appendChild x5 Set x5 = xml.createElement("Zwerg") x5.Text = "Grumpy" x3.appendChild x5 Set x5 = xml.createElement("Zwerg") x5.Text = "Bashful" x3.appendChild x5 Set x5 = xml.createElement("Zwerg") x5.Text = "Happy" x3.appendChild x5 xml.Save "c:\Eigene Dateien\XML\Kapitel06\Uebung01.xml" End Sub
)UDJHQ ]X .DSLWHO
259
Sandini Bib
Abbildung 6.41: Lösung 1
Abbildung 6.42: Lösung 2
260
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
Abbildung 6.43: Lösung 3
Abbildung 6.44: Lösung 4
Frage 2 Wie muss der Code modifiziert werden, damit die anderen drei Dokumente erstellt werden können?
)UDJHQ ]X .DSLWHO
261
Sandini Bib
Frage 3 Was zeigt das Meldungsfenster an? Sub Uebung03a() Dim xml As New MSXML.DOMDocument Dim x3 As MSXML.IXMLDOMElement Dim strText As String xml.Load "c:\Eigene Dateien\XML\Kapitel06\Uebung03.xml" For Each x3 In xml.documentElement.childNodes strText = strText & vbCr & x3.Text Next MsgBox strText End Sub 1. Das gesamte XML-Dokument 2. Alle Attribute des XML-Dokuments 3. Alle Knotennamen des XML-Dokuments 4. Die Namen der Kindknoten des Wurzelelements des XML-Doku-
ments 5. Die Inhalte der Kindknoten des Wurzelelements des XML-Doku-
ments 6. Alle Elemente des Dokuments
262
;0/'RNXPHQWH PLW 9%$ HU]HXJHQ
Sandini Bib
7
le
n e rn
Transformationen von XML mit dem DOM
Manchmal ist es nötig, dass automatisch generierte Dokumente in ihrem Inhalt oder in ihrer Struktur verändert werden. Auch dies kann mit einigen VBA-Befehlen, oder genauer: mit dem DOM, erledigt werden. Welche Möglichkeiten VBA zur Verfügung stellt und wie sie angewendet werden, wird in diesem Kapitel beschrieben. An zwei Beispielen aus Excel und Access wird vorgestellt, wie eine im XML-Format gespeicherte Datei transformiert werden kann, damit sie die gewünschte Gestalt hat. Die Methode appendChild wurde schon im letzten Kapitel vorgestellt: Mit ihrer Hilfe können Elemente an das Wurzelelement oder an andere Elemente angefügt werden: Dim xml As New MSXML.DOMDocument Dim xmlElement As MSXML.IXMLDOMElement xml.Load "c:\Eigene Dateien\XML\Kapitel07\test01.xml" Set xmlElement = xml.createElement("NeuesElement") xml.documentElement.appendChild(xmlElement).Text = _ "Hallo, ist noch wer da?" xml.Save "c:\Eigene Dateien\XML\Kapitel07\test02.xml"
Diese Befehle werden ausführlich im Kapitel 6 beschrieben.
263
Sandini Bib
Abbildung 7.1: Das neue Element wird ans Wurzelelement angehängt
Analog kann das Element mit der Methode removeChild gelöscht werden: Dim xml As New MSXML.DOMDocument Dim xmlElement As MSXML.IXMLDOMElement xml.Load "c:\Eigene Dateien\XML\Kapitel07\test02.xml" Set xmlElement = xml.documentElement.lastChild xml.documentElement.removeChild xmlElement xml.Save "c:\Eigene Dateien\XML\Kapitel07\test03.xml"
Die Methode appendChild setzt das neue Element immer ans Ende der Knotenliste. Soll es an eine bestimmte Position gesetzt werden, dann kann die Methode insertBefore verwendet werden. Beide Methoden geben einen Knoten zurück. Im folgenden Beispiel wird das neue Element am Anfang des Wurzelelements eingefügt: Dim xml As New MSXML.DOMDocument Dim xmlElement As MSXML.IXMLDOMElement xml.Load "c:\Eigene Dateien\XML\Kapitel07\test04.xml" Set xmlElement = xml.createElement("NeuesElement") xml.documentElement.insertBefore(xmlElement, _ xml.documentElement.childNodes(0)).Text = _ "Hallo, ist noch wer da?" xml.Save "c:\Eigene Dateien\XML\Kapitel07\test05.xml"
264
7UDQVIRUPDWLRQHQ YRQ ;0/ PLW GHP '20
Sandini Bib
Abbildung 7.2: Ein neues Element wird am Anfang eingefügt
Fasst man die beiden Methoden insertBefore und removeChild zusammen, dann kann man damit leicht ein Element ersetzen: Sub Ersetze01(xmlBossElement As MSXML.IXMLDOMElement, _ xmlAltesElement As MSXML.IXMLDOMElement, _ xmlNeuesElement As MSXML.IXMLDOMElement) xmlBossElement.insertBefore xmlNeuesElement, _ xmlAltesElement xmlBossElement.removeChild xmlAltesElement End Sub
Diese Prozedur wird von anderer Stelle aufgerufen, beispielsweise so: Sub XML_Bewegung01() Dim xml As New MSXML.DOMDocument Dim xmlElementErstes As MSXML.IXMLDOMElement Dim xmlElementLetztes As MSXML.IXMLDOMElement xml.Load "c:\Eigene Dateien\XML\Kapitel07\test04.xml" Set xmlElementLetztes = xml.documentElement.LastChild Set xmlElementErstes = xml.documentElement.FirstChild Call Ersetze01(xml.documentElement, xmlElementErstes, _ xmlElementLetztes) xml.Save "c:\Eigene Dateien\XML\Kapitel07\test06.xml" End Sub
265
Sandini Bib
Abbildung 7.3: Die Originaldatei
Abbildung 7.4: Das letzte Element ist an die Stelle des ersten gewandert
266
7UDQVIRUPDWLRQHQ YRQ ;0/ PLW GHP '20
Sandini Bib
Es geht mit der Methode replaceChild eleganter: Sub Ersetze02(xmlBossElement As MSXML.IXMLDOMElement, _ xmlAltesElement As MSXML.IXMLDOMElement, _ xmlNeuesElement As MSXML.IXMLDOMElement) xmlBossElement.replaceChild xmlNeuesElement, _ xmlAltesElement End Sub
Achtung: Das XML-Dokument muss bei der Methode replaceChild mindestens drei Elemente haben. Dagegen arbeitet die selbst definierte Prozedur Ersetze01 auch mit zwei, ja sogar mit einem Element, welches nach Ablauf gelöscht ist. Während mit insertBefore ein Knoten verschoben wird, kann mit der Methode cloneNode ein Knoten kopiert werden: Sub KopiereKnoten(xmlBossElement As MSXML.IXMLDOMElement, _ xmlAltesElement As MSXML.IXMLDOMElement) Dim xmlNeuesElement As MSXML.IXMLDOMElement Set xmlNeuesElement = xmlAltesElement.CloneNode(True) xmlBossElement.appendChild xmlNeuesElement End Sub
Die Methode cloneNode verlangt einen Parameter deep („True“ oder „False“), der festlegt, ob die Kindknoten mitgeklont werden oder nicht.
Abbildung 7.5: Das Ergebnis des Duplizierens (Klonens)
267
Sandini Bib
Mit dieser Prozedur wird der letzte Knoten dupliziert und steht nun zwei Mal hintereinander im XML-Dokument. Soll er dagegen am Ende und am Anfang stehen, dann ist der Code um folgende Zeile zu erweitern: xmlBossElement.insertBefore xmlNeuesElement, _ xmlBossElement.childNodes(0)
Vielleicht mag der eine oder andere Leser nun erstaunt kommentieren, dass XML-Transformationen ja eine lustige Sache sind, aber möglicherweise keinen praktischen Sinn haben. Denn – wenn ein XML-Dokument erzeugt wurde, so kann man beim Erzeugen schon auf die Daten und auf die Ebene in der Hierarchie achten, in welcher sich die Daten befinden. Kann man das immer? Was ist, wenn Sie von anderen Menschen oder Maschinen XML-Dokumente erhalten, die Ihnen in dieser Form nicht „gefallen“? So wie in Kapitel 4 beschrieben wurde, wie mit Hilfsmitteln aus Excel und Access Datenbanken in ihrem Inhalt und in ihrer Struktur manipuliert werden können, so können mit Hilfe der DOM-Transformationen Daten restrukturiert werden.
7.1
Excel
Anhand eines simplen Beispiels können Sie das oben Beschriebene ermessen, wenn Sie Office 2002 besitzen. Denn dann können Sie aus Excel und aus Access direkt die Daten als XML speichern. Allerdings speichern beide Programme (vor allem Excel) zu viele Informationen – mehr als benötigt werden.
Abbildung 7.6: Excel XP kann Tabellen nach XML exportieren
268
7UDQVIRUPDWLRQHQ YRQ ;0/ PLW GHP '20
Sandini Bib
Abbildung 7.7: Das Ergebnis beim Öffnen – es werden zu viele Informationen gespeichert
Ein Excel-Dokument wird als XML-Dokument gespeichert. Betrachtet man den Kopf, so fallen die Knoten „DocumentProperties“, „OfficeDocumentSettings“, „ExcelWorkbook“ und „Styles“ auf. Sie werden als Erste gelöscht: Sub Excel_XML_Putzen() Dim xml As New MSXML.DOMDocument Dim xmlKnoten As MSXML.IXMLDOMNode xml.Load "c:\Eigene Dateien\XML\Kapitel07\artikel-tabelle01.xml" For Each xmlKnoten In xml.documentElement.childNodes Select Case xmlKnoten.baseName Case "DocumentProperties", "OfficeDocumentSettings", _ "ExcelWorkbook", "Styles" xml.documentElement.removeChild xmlKnoten End Select Next xml.Save "c:\Eigene Dateien\XML\ Kapitel07\ artikel-tabelle01.xml" End Sub
([FHO
269
Sandini Bib
Abbildung 7.8: Putzen – der erste Schritt
Anschließend wird bis auf die Ebene der Zellen vorgedrungen und die Inhalte des Tags „Data“ werden an das Ende des Dokuments kopiert. Danach werden sie gelöscht: Dim Dim Dim Dim Dim
xmlKnotenE2 As MSXML.IXMLDOMNode xmlKnotenE3 As MSXML.IXMLDOMNode xmlKnotenE4 As MSXML.IXMLDOMNode xmlKnotenE5 As MSXML.IXMLDOMNode xmlKnotenNeu As MSXML.IXMLDOMNode
... Case Else If xmlKnoten.hasChildNodes = True Then For Each xmlKnotenE2 In xmlKnoten.childNodes ' Ebene der Tabelle If xmlKnotenE2.hasChildNodes = True Then For Each xmlKnotenE3 In xmlKnotenE2.childNodes ' Ebene der Zeilen If xmlKnotenE3.hasChildNodes = True Then For Each xmlKnotenE4 In _ xmlKnotenE3.childNodes ' Ebene der Zellen If xmlKnotenE4.hasChildNodes = _ True Then For Each xmlKnotenE5 In _
270
7UDQVIRUPDWLRQHQ YRQ ;0/ PLW GHP '20
Sandini Bib xmlKnotenE4.childNodes ' Ebene der Inhalte If xmlKnotenE5.baseName = _ "Data" Then Set xmlKnotenNeu = _ xml.createElement("dummy") xmlKnotenNeu.Text = _ xmlKnotenE5.Text xml.documentElement.appendChild xmlKnotenNeu xmlKnotenE4.removeChild _ xmlKnotenE5 End If Next End If Next End If Next End If Next End If End Select Next
Abbildung 7.9: Im oberen Teil befinden sich weggeblendet die alten Elemente, unten die neu erzeugten
([FHO
271
Sandini Bib
Sicherlich wäre es eleganter, dieses Beispiel rekursiv zu programmieren. Aber da die Anzahl der Ebenen feststeht und da möglicherweise bestimmte Elemente weiterverarbeitet werden, habe ich mich für diese Variante entschieden. Das Ergebnis ist schon recht ordentlich: Die Buchtitel stehen nun als Unterelemente des Wurzelelements direkt untereinander am Ende des Dokuments. Nun können die alten Ebenen gelöscht werden, da sie jetzt keine relevanten Daten mehr enthalten: For Each xmlKnoten In xml.documentElement.childNodes Select Case xmlKnoten.baseName Case "Worksheet" xml.documentElement.removeChild xmlKnoten End Select Next
Abbildung 7.10: Die reinen Daten – noch ohne Struktur
Damit wurde eine Transformation ausgeführt, nach der die Daten direkt untereinander stehen. Um sie nun in eine logische Reihenfolge zu bringen, könnte man sie paarweise gruppieren (die Zahl 2 ergibt sich hier durch Betrachten der Originaldaten.
272
7UDQVIRUPDWLRQHQ YRQ ;0/ PLW GHP '20
Sandini Bib strSpalte1 = xml.documentElement.childNodes(0).Text strSpalte2 = xml.documentElement.childNodes(1).Text For lngZähler = 2 To _ xml.documentElement.childNodes.Length - 1 If lngZähler Mod 2 = 0 Then Set xmlKnotenNeu = xml.createElement(strSpalte1) Else Set xmlKnotenNeu = xml.createElement(strSpalte2) End If xmlKnotenNeu.Text = _ xml.documentElement.childNodes(lngZähler).Text xml.documentElement.appendChild xmlKnotenNeu Next
Und zum Schluss werden die Dummy-Daten gelöscht: For lngZähler = xml.documentElement.childNodes.Length - 1 _ To 0 Step -1 Select Case xml.documentElement. _ childNodes(lngZähler).baseName Case "dummy" xml.documentElement.removeChild _ xml.documentElement.childNodes(lngZähler) End Select Next
Und damit stehen die Daten in der gewünschten Form zur Verfügung. Das heißt: Der Benutzer kann aus Excel die Daten mit einem Klick auf DATEI / SPEICHERN UNTER / „Dateityp: XML-Kalkulationstabelle“ exportieren. Wenn man das Beispiel ein wenig modifiziert, dann können nun alle Daten in eine „vernünftige“ Struktur gebracht werden, das heißt in eine Form, wie sie im Browser gut darstellbar ist. Voraussetzung ist lediglich, dass der Aufbau der Tabelle wie in unserem Beispiel aussieht. In der ersten Zeile stehen die Feldnamen und direkt darunter die Daten – ohne Leerzeilen und ohne Leerspalten.
([FHO
273
Sandini Bib
Abbildung 7.11: Das fertige Ergebnis
7.2
Access
Für Access spielen die Transformationen keine so große Rolle wie für Excel. Zum einen können in Access leicht in einer Abfrage die Felder, Filter, Sortierungen, Verknüpfungen und Berechnungen festgelegt werden. Andererseits stellt Access für den XML-Export bereits einen kleinen Assistenten zur Verfügung, der die Daten in geeigneter Weise abspeichert:
Abbildung 7.12: In der Abfrage werden die Voreinstellungen vorgenommen
274
7UDQVIRUPDWLRQHQ YRQ ;0/ PLW GHP '20
Sandini Bib
Abbildung 7.13: Ein Assistent hilft beim Export der Daten
..088
275
Sandini Bib
Abbildung 7.14: Das Ergebnis des Exports
Übrigens steht für den Export einer Tabelle oder einer Abfrage natürlich auch ein VBA-Befehl zur Verfügung. Erstaunlicherweise ist es nicht DoCmd.TransferText oder DoCmd.TransferSpreadShhet mit dem Parameter „XML“, sondern es wird das Objekt Application und die Methode ExportXML verwendet: Application.ExportXML
Es besitzt folgende Parameter: Parameter
Beschreibung
ObjectType
Das zu exportierende Access-Objekt: acExportDataAccessPage, acExportForm, acExportFunction, acExportQuery, acExportReport, acExportServerView, acExportStoredProcedure oder acExportTable
DataSource
Der Name des zu exportierenden Access-Objekts
DataTarget
Der Dateiname und der Pfad für die exportierte Datei
DataTransform
Der Name der XSL-Datei, die für die Daten ausgeführt werden soll, bevor sie in die Zieldatei geschrieben werden
SchemaTarget
Der Dateiname und der Pfad für die exportierten Schemainformationen
Tabelle 7.1: Die Parameter der Methode ExportXML
276
7UDQVIRUPDWLRQHQ YRQ ;0/ PLW GHP '20
Sandini Bib Parameter
Beschreibung
SchemaFormat
Das Format, in dem Schemainformation exportiert werden: acSchemaNone oder acSchemaXSD
SchemaTransform
Der Name der XSL-Datei, die für die Schemainformationen ausgeführt werden soll, bevor sie in die Zieldatei geschrieben werden
PresentationTarget
Der Dateiname und der Pfad für die exportierten Präsentationsinformationen. Wird dieses Argument nicht angegeben, werden die Präsentationsinformationen nicht exportiert
PresentationTransform
Der Name der XSL-Datei, die für die Präsentationsinformationen ausgeführt werden soll, bevor sie in die Zieldatei geschrieben werden
ImageTarget
Der Pfad für exportierte Bilder
LiveReportSource
Verbindungsinformationen für einen Bericht, der Livedaten enthält. Dabei kann es sich um einen Verweis auf eine ODC-Datei oder eine XMLSQL-Anforderung handeln. Dieses Argument wird nur beachtet, wenn ObjectType gleich acExportReport ist.
Encoding
Textcodierung: acEUCJ, acUCS2, acUCS4, acUTF16 oder acUTF8 (Standard)
OtherFlags
Eine Bitmaske, die andere Verhaltensweisen im Zusammenhang mit dem Export in das XML-Format definiert
Tabelle 7.1: Die Parameter der Methode ExportXML (Forts.)
Es genügt folgende Zeile: Sub XMLExport() Application.ExportXML _ ObjectType:=acExportTable, _ DataSource:="qry_SortMitgliederNachName", _ DataTarget:="c:\Eigene Dateien\XML\Kapitel07\test19.xml" End Sub
Das Ergebnis sieht aus wie in Abbildung 7.14. Angenommen die Mitgliedsnummer soll in den Namen des Elements aufgenommen werden. Und das Geschlecht wird zu einem Attribut. Dann kann dies mit einer Transformation durchgeführt werden. Im ersten Schritt wird das Dokument geladen und die Anrede wird als Attribut in den Namen aufgenommen: ... xml.async = False xml.Load "c:\Eigene Dateien\XML\Kapitel07\" & _ „qry_SortMitgliederNachName.xml" For Each x0 In xml.documentElement.childNodes x0.setAttribute "Geschlecht", x0.childNodes(1).Text Next
..088
277
Sandini Bib
Danach können die überflüssigen Elemente gelöscht werden. Es ist das zweite Element, also Nummer 1: For Each x0 In xml.documentElement.childNodes x0.removeChild x0.childNodes(1) Next
Nun werden alle Elemente erneut durchlaufen. Wird ein Element mit dem Namen „qry_SortMitgliederNachName“ gefunden, dann wird ein neues Element am Ende der Liste erzeugt. Es trägt den Namen, der sich aus dem Text „Mitglied“ und der Mitgliedsnummer zusammensetzt. Die Kindelemente außer der Mitgliedsnummer werden vom alten Element in das neue kopiert. For Each x0 In xml.documentElement.childNodes If x0.baseName = "qry_SortMitgliederNachName" Then Set xNeu = xml.createElement _ ("Mitglied" & x0.childNodes(0).Text) xml.documentElement.appendChild xNeu For Each x1 In x0.childNodes If x1.baseName "tMitMitgliedsnummer" Then xNeu.appendChild x1 End If Next End If Next
Im letzten Schritt werden die alten Elemente gelöscht. For Each x0 In xml.documentElement.childNodes If x0.baseName = "qry_SortMitgliederNachName" Then xml.documentElement.removeChild x0 End If Next
Vergessen Sie nicht, das neue Dokument zu speichern: xml.Save "c:\Eigene Dateien\XML\Kapitel07\test11.xml"
278
7UDQVIRUPDWLRQHQ YRQ ;0/ PLW GHP '20
Sandini Bib
Abbildung 7.15: Das Ergebnis der Transformation
Übrigens: Da es sich hier um fast 4000 Datensätze handelt, das heißt um eine XML-Datei mit einer Dateigröße von 1,3 MByte, die in eine Datei von 900 KByte transformiert wird, wollte ich wissen, wie lange mein Rechner hierfür benötigt. Das Ergebnis kann mit Hilfe der Timer-Funktion ermittelt werden: Dim sngZeitAnfang As Single Dim sngZeitEnde As Single sngZeitAnfang = Timer ... sngZeitEnde = Timer MsgBox "Die Datei " & vbCr & _ """" & xml.url & """" & vbCr & _ " wurde erfolgreich transformiert." _ & vbCr & vbCr & _ "Dazu wurden " & Format(sngZeitEnde - _ sngZeitAnfang, "0.00") & _ " Sekunden benötigt." End Sub
Mein Rechner benötigt fast sieben Sekunden:
..088
279
Sandini Bib
Abbildung 7.16: Für die bewegte Datenmenge ganz schön schnell ...
7.3
Zusammenfassung
Manchmal ist es nötig, dass automatisch generierte Dokumente in ihrem Inhalt oder in ihrer Struktur verändert werden müssen. Nicht immer können XML-Dateien von Grund auf geplant und angelegt werden. Dies kann mit einigen VBA-Befehlen, oder genauer: mit dem DOM, erledigt werden. Welche Möglichkeiten VBA hierzu zur Verfügung stellt und wie sie angewendet werden, wurde in diesem Kapitel vorgestellt. An zwei Beispielen aus Excel und Access wurde gezeigt, wie eine im XML-Format gespeicherte Datei transformiert werden kann, damit sie die gewünschte Gestalt hat.
7.4
Fragen zu Kapitel 7
Frage 1 Gegeben sei ein XML-Dokument: ]> Anton Berti Conni Det Edi Fritzchen
Welches der folgenden Dokumente wird nach der Transformation erzeugt:
280
7UDQVIRUPDWLRQHQ YRQ ;0/ PLW GHP '20
Sandini Bib Sub Uebung01() Dim xml As New MSXML.DOMDocument Dim x0 As MSXML.IXMLDOMNode xml.Load "C:\Eigene Dateien\XML\Kapitel07\Uebung01.xml" With xml.documentElement .removeChild .FirstChild .replaceChild .childNodes(4), .childNodes(2) Set x0 = .LastChild.CloneNode(True) .appendChild x0 .insertBefore x0, .FirstChild End With End Sub
Abbildung 7.17: Lösung 1
Abbildung 7.18: Lösung 2
)UDJHQ ]X .DSLWHO
281
Sandini Bib
Abbildung 7.19: Lösung 3
Abbildung 7.20: Lösung 4
Frage 2 Wie können die übrigen drei Dateien erzeugt werden? Frage 3 Was zeigt das Meldungsfenster des folgenden Dokuments an? Sub Uebung03a() Dim xml As New MSXML.DOMDocument Dim x3 As MSXML.IXMLDOMAttribute Dim strText As String
282
7UDQVIRUPDWLRQHQ YRQ ;0/ PLW GHP '20
Sandini Bib xml.Load "c:\Eigene Dateien\XML\Kapitel07\Uebung01.xml" For Each x3 In xml.documentElement.childNodes strText = strText & vbCr & x3.Text Next MsgBox strText End Sub 1. Das gesamte XML-Dokument 2. Alle Attribute des XML-Dokuments 3. Alle Knotennamen des XML-Dokuments 4. Die Namen der Kindknoten des Wurzelelements des XML-
Dokuments 5. Die Inhalte der Kindknoten des Wurzelelements des XML-
Dokuments 6. Alle Elemente des Dokuments 7. Eine Fehlermeldung bei der Zeile
For Each x3 In xml.documentElement.childNodes 8. Eine Fehlermeldung bei der Zeile
strText = strText & vbCr & x3.Text 9. Eine leere Zeichenkette, da das Dokument keine Attribute besitzt
)UDJHQ ]X .DSLWHO
283
Sandini Bib
Sandini Bib
le
n e rn
8
XML- und XSL-Dateien mit VBA erzeugen
8.1
Excel
Mit dem VBA-Wissen aus Kapitel 6 und 7 und dem Verständnis, wie XML und XSL funktionieren (Kapitel 1 bis 3), können nun die Daten aus Excel und Access exportiert werden. Damit der Export dynamisch gehalten wird, wird dem Benutzer eine Userform zur Verfügung gestellt. In ihr kann er auswählen, ob er alle Informationen oder nur bestimmte Felder exportieren möchte. Eines der Felder kann er hervorheben – dabei hat er die Auswahl zwischen „fett“, „großer Schrift“ und „rot“, wobei Mehrfachkombinationen möglich sind. Unabhängig davon kann die XML-Datei nach einem Feld sortiert werden. Befinden sich Bild-Informationen in der Datei, so sollen nicht die Bild-Dateinamen angezeigt werden, sondern die Bilder selbst. Da der Speicherort in der Regel von dem Speicherort der XML-Datei abweicht, kann der Benutzer angeben, in welchem Ordner sich die Bilder befinden. Um das Beispiel flexibel zu halten, wird die Beschriftung der Optionsfelder nicht auf der Userform eingegeben, sondern aus den Feldnamen ausgelesen. Zuerst erfolgt die Variablendeklaration. Beachten Sie, dass die vier Objektvariablen global deklariert wurden, da sie an anderen Stellen der Userform ebenfalls verwendet werden: Private Sub UserForm_Initialize() Dim intZähler As Integer Set xlApp = Application Set xlMappe = xlApp.ActiveWorkbook Set xlBlatt = xlMappe.Worksheets(1) Set xlZelle = xlBlatt.Range("A1") intSpalten = xlZelle.CurrentRegion.Columns.Count
([FHO
285
Sandini Bib
Zuerst werden das Listenfeld und das Kombinationsfeld mit den Feldnamen gefüllt: For intZähler = 1 To intSpalten Me.lstFelder.AddItem _ xlZelle.Offset(0, intZähler - 1).Value Me.cboBild.AddItem _ xlZelle.Offset(0, intZähler - 1).Value Next Me.cboBild.ListIndex = 0 Me.optKeineBilder.Value = True
Anschließend werden die Optionsfelder mit den Feldnamen gefüllt. Für sie werden jeweils acht Radiobuttons bereitgestellt: If intSpalten < 8 Then For intZähler = 1 To intSpalten Me.Controls("optSort" & intZähler).Caption = _ "sortiert nach: " & vbLf & _ Me.lstFelder.List(intZähler - 1) Me.Controls("optHervor" & intZähler).Caption = _ Me.lstFelder.List(intZähler - 1) Next For intZähler = intSpalten + 1 To 8 Me.Controls("optSort" & intZähler).Visible = False Me.Controls("optHervor" & intZähler).Visible = False Next Else For intZähler = 1 To 8 Me.Controls("optSort" & intZähler).Caption = _ "sortiert nach: " & vbLf & _ Me.lstFelder.List(intZähler - 1) Me.Controls("optHervor" & intZähler).Caption = _ Me.lstFelder.List(intZähler - 1) Next End If End Sub
286
;0/ XQG ;6/'DWHLHQ PLW 9%$ HU]HXJHQ
Sandini Bib
Abbildung 8.1: Eine Userform dient zum leichten und schnellen Export
Mit Hilfe der beiden Befehlsschaltflächen kann der Speicherpfad für die XML- und XSL-Datei beziehungsweise für die Bilder geändert werden. Es wird eine API-Funktion verwendet, die von der Funktion „DateienErmitteln“ aufgerufen wird. Dies soll an dieser Stelle nicht erläutert werden – das Beispiel befindet sich auf der CD-ROM und kann dort eingesehen werden. Private Sub cmdPfad_Click() DateienErmitteln (ActiveWorkbook.Worksheets _ (2).Range("A2")) End Sub
Und nun die eigentliche Auswertung. Es werden eine Reihe von Variablen benötigt: Private Sub cmdExport_Click() Dim xml As New MSXML.DOMDocument Dim x0 As MSXML.IXMLDOMElement Dim x1 As MSXML.IXMLDOMElement Dim x2 As MSXML.IXMLDOMElement Dim x3 As MSXML.IXMLDOMElement Dim x4 As MSXML.IXMLDOMElement Dim x5 As MSXML.IXMLDOMElement Dim x6 As MSXML.IXMLDOMElement
([FHO
287
Sandini Bib Dim xP As MSXML.IXMLDOMProcessingInstruction Dim Dim Dim Dim Dim Dim Dim Dim
strKnotennamen() As String intZähler As Integer lngZeilenZähler As Long intZeichenzähler As Integer strZeichen As String bKnoten As Boolean strPfad As String strBilderPfad As String
Dim strHervor As String Const NORMAL = "color:black; font-family:Times; font-size:12pt"
Es wird überprüft, ob der Benutzer einen Dateinamen eingegeben hat: If Me.txtDateiname.Value = "" Then MsgBox "Es wurde kein Dateiname eingegeben!", vbInformation Me.txtDateiname.SetFocus Exit Sub End If
Die Speicherpfade werden überprüft. Dieser Teil soll hier nicht weiter erläutert werden. strPfad = DateienErmitteln(ActiveWorkbook.Worksheets _ (2).Range("A2")) If Right(strPfad, 1) "\" Then strPfad = strPfad & "\" End If If Me.optBildVorhanden.Value = True Then strBilderPfad = DateienErmitteln(ActiveWorkbook. _ Worksheets(2).Range("A4")) If Right(strBilderPfad, 1) "\" Then strBilderPfad = strBilderPfad & "\" End If If Dir(strPfad, vbDirectory) = "" Then MsgBox "Der angegebene Pfad" & vbCr & _ strPfad & vbCr & "existiert nicht.", _ vbInformation Exit Sub End If End If If Dir(strPfad, vbDirectory) = "" Then MsgBox "Der angegebene Pfad" & vbCr & _ strPfad & vbCr & "existiert nicht.", _ vbInformation Exit Sub End If
288
;0/ XQG ;6/'DWHLHQ PLW 9%$ HU]HXJHQ
Sandini Bib
Der Array strKnotennamen wird verwendet, um die Feldnamen der Exceltabelle zu speichern. Wurde das Optionsfeld „Alle Informationen“ ausgewählt, dann werden alle Feldnamen gespeichert, wurden nur einige ausgewählt, dann werden nur diese im Feld strKnotennamen gespeichert. Dazu läuft eine Schleife durch alle Einträge des Listenfelds und überprüft, ob sie markiert sind. ReDim strKnotennamen(intSpalten) If Me.optAlle.Value = True Then For intZähler = 1 To intSpalten strKnotennamen(intZähler) = _ XMLZeichen(xlZelle.Offset(0, _ intZähler - 1).Value) Next Else bKnoten = False For intZähler = 1 To intSpalten If Me.lstFelder.Selected(intZähler - 1) = True Then strKnotennamen(intZähler) = _ XMLZeichen(Me.lstFelder.List(intZähler - 1)) bKnoten = True Else strKnotennamen(intZähler) = "" End If Next End If
Hat der Benutzer eine selektive Auswahl getroffen, dann wird die Variable bKnoten auf „False“ gesetzt. Wurde in der Liste ein markierter Eintrag gefunden, dann wird die Variable auf „True“ gesetzt. Wurde kein markierter Eintrag gefunden, dann wird er darauf hingewiesen: If Me.optAuswahl.Value = True And bKnoten = False Then MsgBox "Bitte wählen Sie ein zu exportierendes Feld aus!", _ vbInformation Exit Sub End If
Nun beginnt der eigentliche Datenexport. Er besteht aus zwei Teilen. Zuerst werden die Informationen in eine XML-Datei geschrieben. Die Informationen wurden alle in Kapitel 6 ausführlich beschrieben. Beachten Sie bitte, dass auch die Processing Instructions in die XML-Datei „hineinprogrammiert“ werden. Wichtig ist die zweite PI, da sie auf die XSLDatei verweist, welche die XML-Datei formatiert. Der Dateiname der XSL-Datei unterscheidet sich von der XML-Datei dadurch, dass ein „XSL“ vorangestellt wird und dass die Endung „.xsl“ lautet.
([FHO
289
Sandini Bib xml.loadXML "" Set xP = xml.createProcessingInstruction _ ("xml", "version=""1.0"" encoding=""ISO-8859-1""") xml.appendChild xP Set xP = xml.createProcessingInstruction _ ("xml-stylesheet", "href=""XSL" & _ Me.txtDateiname.Value & ".xsl"" type=""text/xsl""") xml.appendChild xP
Das Wurzelelement wird „Liste“ genannt. Set x0 = xml.createElement("Liste") xml.appendChild x0
Jeder Knoten wird „Buch“ genannt. Es werden so viele Buch-Knoten erzeugt, wie die Excel-Tabelle Datensätze beinhaltet. Jeder Knoten erhält Kindknoten, welche die entsprechenden Feldnamen besitzen und mit den Daten aus den korrespondierenden Feldern gefüllt werden. For lngZeilenZähler = 1 To _ xlZelle.CurrentRegion.Rows.Count - 1 Set x1 = xml.createElement("Buch") xml.documentElement.appendChild x1 For intZähler = 1 To UBound(strKnotennamen) If strKnotennamen(intZähler) "" Then Set x2 = _ xml.createElement(strKnotennamen(intZähler)) x2.Text = xlZelle.Offset(lngZeilenZähler, _ intZähler - 1).Value x1.appendChild x2 End If Next intZähler Next lngZeilenZähler
Mit der Methode append werden Knoten an die richtigen Elternknoten gehängt. Nun kann das XML-Dokument gespeichert werden: xml.Save strPfad & Me.txtDateiname.Value & ".xml"
Der zweite Teil beginnt. Eine zweite Datei für die Formatierung wird erzeugt. Sie besitzt einige festgelegte Elemente. Da der Wurzelknoten „Liste“ und die Kindknoten „Buch“ per Code fest vorgegeben waren, werden sie nun so in das Dokument hineingeschrieben. ' -- zweiter Teil: die XSL-Datei xml.loadXML "" Set x1 = xml.createElement("xsl:stylesheet") x1.setAttribute "xmlns:xsl", "http://www.w3.org/TR/WD-xsl" Set x2 = xml.createElement("xsl:template") Set x3 = xml.createElement("xsl:for-each") x3.setAttribute "select", "Liste/Buch"
290
;0/ XQG ;6/'DWHLHQ PLW 9%$ HU]HXJHQ
Sandini Bib
Die selbst erstellte Funktion WerWirdHervorgehoben überprüft, welches Optionsfeld innerhalb des Rahmens hervorgehoben wird: Function WerWirdHervorgehoben(fraRahmen As Frame) As String Dim ctl As Control For Each ctl In fraRahmen.Controls If Left(ctl.Name, 3) = "opt" Then If ctl.Value = True Then If Left(ctl.Caption, 8) = "sortiert" Then WerWirdHervorgehoben = _ XMLZeichen(Mid(ctl.Caption, 18)) Else WerWirdHervorgehoben = XMLZeichen(ctl.Caption) End If End If End If Next End Function
Außerdem wird noch eine zweite Funktion benötigt. Sollten sich in der Überschriftenspalte Feldnamen befinden, die nicht den XML-Konventionen genügen, so werden sie abgeschnitten. Diese Funktion wird „XMLZeichen“ genannt und sieht wie folgt aus: Function XMLZeichen(strTitel As String) As String Dim intZeichenzähler As Integer Dim strZeichen As String Dim strDummy As String For intZeichenzähler = 1 To Len(strTitel) strZeichen = Mid(strTitel, _ intZeichenzähler, 1) Select Case Asc(strZeichen) Case Asc("0") To Asc("9") strDummy = strDummy & strZeichen Case Asc("A") To Asc("Z") strDummy = strDummy & strZeichen Case Asc("a") To Asc("z") strDummy = strDummy & strZeichen Case Else Exit For End Select Next XMLZeichen = strDummy End Function
([FHO
291
Sandini Bib
Wurde also auf der Userform die Option eingeschaltet, dass nach einem bestimmten Feld sortiert werden kann, dann wird dies in das Attribut des for-each-Tags geschrieben. If Me.optKeineSortierung.Value = False Then x3.setAttribute "order-by", "+ " & _ WerWirdHervorgehoben(fraSortierung) End If
Nun werden die Knoten in der korrekten Reihenfolge aneinander gehängt: xml.appendChild x1 x1.appendChild x2 x2.appendChild x3
Es wird überprüft, ob eines der Kontrollkästchen zur Hervorhebung eingeschaltet wurde. Die verschiedenen ausgewählten Eigenschaften werden in einer Zeichenkette gespeichert. If Me.chkRot.Value = True Then strHervor = "color:red; " Else strHervor = "color:black; " End If If Me.chkGroßeSchrift.Value = True Then strHervor = strHervor & "font-size:24pt; " Else strHervor = strHervor & "font-size:12pt; " End If If Me.chkFett.Value = True Then strHervor = strHervor & "font-weight:bold; " End If strHervor = strHervor & "font-family:Arial"
Eine Schleife durchläuft alle vorhandenen Knotennamen. Da die XSLElemente für Bilder etwas anders aussehen, wird mit der Abfrage begonnen, ob sich Bildnamen im Dokument befinden. For intZähler = 1 To UBound(strKnotennamen) If strKnotennamen(intZähler) "" Then If Me.optKeineBilder.Value = True Then
Falls sich keine Bilder im Dokument befinden, dann wird das Element eingefügt. Falls das nächste Element hervorgehoben wird, so wird das Attribut STYLE eingefügt. Es erhält den Wert, der zuvor in der Variable strHervor gespeichert wurde.
292
;0/ XQG ;6/'DWHLHQ PLW 9%$ HU]HXJHQ
Sandini Bib Set x4 = xml.createElement("DIV") If Me.optKeineHervorhebung.Value = False Then If strKnotennamen(intZähler) = _ WerWirdHervorgehoben(fraHervorhebung) Then x4.setAttribute "STYLE", strHervor End If Else
Wird das entsprechende Element nicht hervorgehoben, so erhält es eine „normale“ Formatierung. Sie wurde in einer Konstanten zu Beginn der Prozedur festgelegt. x4.setAttribute "STYLE", NORMAL End If Set x5 = xml.createElement("xsl:value-of") x5.setAttribute "select", _ strKnotennamen(intZähler) x3.appendChild x4 x4.appendChild x5
Die Knoten werden korrekt aneinander gehängt. Wurden Bilder ausgewählt, so muss für das entsprechende Element der Tag erzeugt werden. Das Attribut „src“ gibt die Quelle des Bildes an, die sich aus dem gespeicherten Pfad und dem Dateinamen, der in der Tabelle steht, zusammensetzt. Else If strKnotennamen(intZähler) = _ XMLZeichen(Me.cboBild.Value) Then Set x4 = xml.createElement("IMG") x4.setAttribute "BORDER", "0" Set x5 = xml.createElement("xsl:attribute") x5.setAttribute "name", "src" x5.Text = strBilderPfad Set x6 = xml.createElement("xsl:value-of") x6.setAttribute "select", strKnotennamen(intZähler) x3.appendChild x4 x4.appendChild x5 x5.appendChild x6 Else
Elemente, die keine Bilder sind, werden wie oben behandelt. Man könnte (und sollte) diesen Code-Teil auslagern, damit man ihn nicht zwei Mal schreiben muss.
([FHO
293
Sandini Bib Set x4 = xml.createElement("DIV") If Me.optKeineHervorhebung.Value = False Then If strKnotennamen(intZähler) = _ WerWirdHervorgehoben(fraHervorhebung) Then x4.setAttribute "STYLE", strHervor End If Else x4.setAttribute "STYLE", NORMAL End If Set x5 = xml.createElement("xsl:value-of") x5.setAttribute "select", _ strKnotennamen(intZähler) x3.appendChild x4 x4.appendChild x5 End If End If End If Next
Nachdem die XSL-Datei erzeugt wurde, wird sie gespeichert. Heißt das XML-Dokument beispielsweise „Test.xml“, so lautet die zugehörige XSL-Datei „XSLTest.xsl“. Beide werden im gleichen Ordner gespeichert. xml.Save strPfad & "XSL" & Me.txtDateiname.Value & ".xsl"
Der Dialog wird entladen. Unload Me End Sub
Abbildung 8.2: Eine Auswertung ...
294
;0/ XQG ;6/'DWHLHQ PLW 9%$ HU]HXJHQ
Sandini Bib
Abbildung 8.3: ... und das Ergebnis
Abbildung 8.4: Eine andere Auswertung ...
([FHO
295
Sandini Bib
Abbildung 8.5: ... mit ihrem Ergebnis
8.2
Access
Das Gleiche kann man auch in Access durchführen. Dort wird ein ungebundenes Formular mit einigen Steuerelementen erstellt:
Abbildung 8.6: Das neue Access-Formular
296
;0/ XQG ;6/'DWHLHQ PLW 9%$ HU]HXJHQ
Sandini Bib
Die Kontrollkästchen heißen „chk01“, „chk02“, ..., die Bezeichnungsfelder „lbl01“, „lbl02“, ... Letztere werden beim Laden des Formulars beschriftet: Private Sub Form_Open(Cancel As Integer) Dim cat As New ADOX.Catalog Dim rs As ADODB.Recordset Dim fld As ADODB.Field Dim i As Integer Dim j As Integer Dim strListe As String On Error Resume Next Set cat.ActiveConnection = CurrentProject.Connection Set rs = New ADODB.Recordset rs.ActiveConnection = CurrentProject.Connection rs.CursorType = adOpenStatic rs.LockType = adLockOptimistic rs.Open "SELECT * FROM tab_Mitglieder" i = 1 For Each fld In rs.Fields If i
9.1.5 Struktur fortsetzen Das Dokument wird so beendet:
9.1.6 Fehler Folgende Fehler haben sich eingeschlichen: „standalone“ muss mit einem „l“ geschrieben werden. ist falsch geschachtelt.
Beim Tag wurde die Groß- und Kleinschreibung nicht beachtet. Ein
XML-Dokument
darf
nur
ist falsch.
308
/|VXQJHQ
ein
Wurzelelement
haben
–
Sandini Bib
9.2
Kapitel 2
9.2.1 Allgemeine Fragen Folgende Aussagen sind korrekt: 1. Namensräume (Namespaces) stellen sicher, dass gleich lautende Tags
nicht miteinander in Konflikt geraten. 2. Es existieren fünf Entitätsreferenzen: &, <, > ' und
". 3. Neben diesen vordefinierten Entitätsreferenzen können weitere En-
titätsreferenzen erstellt werden. 4. Das abschließende Semikolon ist – anders als in HTML – zwingend
notwendig. 5. Attributwerte müssen – anders als in HTML – in Anführungszeichen
gesetzt werden. 6. Ein Tag kann beliebig viele Attribute besitzen.
9.2.2 Zur DTD 1. Die DTD
oder fehlt fehlt
Dagegen werden beschrieben: 2. Das Dokument
wird durch folgende DTD beschrieben:
.DSLWHO
309
Sandini Bib
9.3
Kapitel 3
9.3.1 Lösung 1 Da die Zeile xsl:for-each fehlt, wird nur ein Mainzelmann angezeigt. Lösung 2 ist die korrekte Antwort.
9.3.2 Lösung 2 Es gibt sicherlich viele Fehler, die zu einer Fehlermeldung oder zu einem leeren Ergebnis führen, beispielsweise, wenn Sie „Mainzelmaennchen“ falsch schreiben oder den Pfad nicht korrekt angeben:
Alle Elemente (Lösung 3) werden angezeigt, wenn der Code wie folgt aussieht:
Das HTML-Element H1 beinhaltet eine Zeilenschaltung. Sollen die Elemente nebeneinander dargestellt werden (Lösung 4), so muss der Tag verwendet werden:
310
/|VXQJHQ
Sandini Bib
9.3.3 Lösung 3 Die XML-Datei muss folgenden Prolog besitzen:
Die css-Datei „Mainzelmaennchen06.css“ hat beispielsweise folgende Gestalt, wenn die Männer untereinander stehen sollen: Mainzelmaennchen { display: block; font-family: Times; font-size: 40pt; font-weight: bold; margin-top: 0; margin-bottom: 6pt
} Sollen die Männer nebeneinander stehen, dann fehlt die Zeile display: block;
9.4
Kapitel 4
Zu Excel
9.4.1 Lösung 1 Beginnen Sie in der Zelle neben Spalte A, also beispielsweise in B1 oder in B2. Mit der Formel =WENN(A1="Zwischensumme :";"x";"")
wird in B1 überprüft, ob in A1 der gesuchte Text steht.
9.4.2 Lösung 2 Diesmal beginne ich in C2. Dort überprüfe ich, ob in A2 der gesuchte Text nicht steht. Und diesmal wird kein „Anzeigetext“ ausgegeben, sondern der Inhalt einer Zelle (A2) wiederholt: =WENN(A2"Zwischensumme :";A2;"")
.DSLWHO
311
Sandini Bib
Statt des Ungleichheitszeichens „“ kann auch die Bedingung umgedreht werden: =WENN(A2="Zwischensumme :";"";A2)
Oder Sie arbeiten mit NICHT, was ein wenig umständlich ist: =WENN(NICHT(A2="Zwischensumme :");A2;"")
9.4.3 Lösung 3 Die Funktion LÄNGE ermittelt die Anzahl der Zeichen. Greifen Sie auf Spalte A zu, so ergibt =LÄNGE(A7)
die Zahl 15, dagegen ergibt =LÄNGE(A7)
die Zahl 0.
9.4.4 Lösung 4 Steht in Spalte D die Länge der Zeichenkette, dann kann mit der Funktion RECHTS eine bestimmte Anzahl Zeichen herausgelöst werden. Da die Zahl aber ein oder zwei Ziffern lang ist, wird von der Länge 1 abgezogen: =RECHTS(C2;D2-1)
Problem: Steht nun in D7 der Wert 0, so wird von rechts -1 Zeichen abgezogen, was zu einem Fehler führt. Dies könnte man mit der Funktion ISTFEHLER abfangen: =WENN(ISTFEHLER(RECHTS(C2;D2-1));"";RECHTS(C2;D2-1))
Oder auch durch eine einfachere Überprüfung: =WENN(D2=0;"";RECHTS(C2;D2-1))
Diese Variante ist sicherlich einfacher, jene greift bei allen möglichen Fehlern, die auftauchen können.
9.4.5 Lösung 5 Soll mit der Zahl weitergerechnet werden, dann muss sie mit Hilfe der Funktion WERT umgewandelt werden: Da allerdings in einigen Zellen der Inhalt "" steht, führt diese Umwandlung zu einem Fehler. Erneut kann (und sollte) überprüft werden: =WENN(E2="";"";WERT(E2))
312
/|VXQJHQ
Sandini Bib
9.4.6 Lösung 6 Diese Aufgabe bedarf doch eigentlich keiner Lösung, oder? =F2*10
Wollen Sie die unschönen Fehler ausblenden, dann erneut mit: =WENN(F2="";"";F2*10)
Oder natürlich wieder mit ISTFEHLER, wie in Lösung 4 beschrieben. Warum nicht auch mit der Funktion ISTZAHL? =WENN(ISTZAHL(F2);F2*10;"")
9.4.7 Lösung 7 Auch dies ist eine einfache Übung. Entweder mit: ="C"&G2
oder mit Hilfe der Funktion VERKETTEN: =VERKETTEN("C";G2)
Oder natürlich wieder so: =WENN(G2="";"";"C"&G2)
9.4.8 Lösung 8 Um das Ergebnis bewahren zu können, müssen Sie das Ergebnis der Formel markieren (bei mir nun die Spalte H), kopieren und mit dem Befehl BEARBEITEN / INHALTE EINFÜGEN mit der Option „Werte“ überschreiben. Nun stehen keine Bezüge auf die anderen Spalten mehr in der Zelle, so dass die ursprünglichen Werte problemlos gelöscht werden können.
9.4.9 Lösung 9 Natürlich geht es auch mit einer Funktion. Etwa mit folgender: =WENN(A2"Zwischensumme :";"C"&WERT(RECHTS(A2;LÄNGE(A2)-1))*10;"")
Übrigens: Solche Monsterformeln erstelle ich niemals in einem Schritt, sondern immer analytisch Stück für Stück, damit ich sie am Ende in einer Zelle „zusammenfassen“ kann. Gerade Anfänger bei Excel-Formeln tun gut daran, das Problem zuerst in seine Bestandteile zu zerlegen, bevor sie in einem Schritt eine solche Formel eingeben.
.DSLWHO
313
Sandini Bib
Abbildung 9.1: Die einzelnen Lösungen nebeneinander
9.4.10 Lösung 10 Mit dem Suchen- und Ersetzen-Assistenten geht es sicherlich einfacher: Ersetze „A“ durch nichts (keine Eingabe). Der Assistent „Text in Spalten“ (im Menü DATEN) stellt im zweiten Schritt die Option zur Verfügung, als Trennzeichen ein „A“ zu verwenden. Die funktioniert in diesem speziellen Fall, da „A“ nur an erster Position vorkommt.
Abbildung 9.2: Der Assistent „Text in Spalten“ – Schritt 2
314
/|VXQJHQ
Sandini Bib
Zu Access
9.4.11 Lösung 1 Die Spalte ID ist mit Sicherheit vom Felddatentyp „AutoWert“ deklariert und besitzt einen Primärschlüssel. Die anderen beiden Spalten sind „Text“, was man leicht an der Linksbündigkeit feststellen kann.
9.4.12 Lösung 2 Die Anzahl der auftretenden Texte kann in einer Abfrage ermittelt werden. Dort wird zwei Mal das Feld „Wirkung“ nach unten gezogen und die Funktionen über das Symbol „?“ eingeschaltet. Wirkung wird zum einen gruppiert, zum anderen gezählt, das heißt die Funktion „Anzahl“ wird ausgewählt.
Abbildung 9.3: Die Abfrage in der Entwurfsansicht
.DSLWHO
315
Sandini Bib
Abbildung 9.4: Das Ergebnis der Abfrage
9.4.13 Lösung 3 In einer zweiten Abfrage wird in der Entwurfsansicht die Spalte „Zusätze“ nach unten geholt. Daneben wird ein neues Feld definiert, beispielsweise mit dem Namen „Zusätze_Ohne_E“. Da alle Zahlen aus drei Ziffern bestehen, muss nicht kompliziert mit Zusätze_Ohne_E: Rechts([Zusätze];Länge([Zusätze])InStr([Zusätze];" "))
gearbeitet werden. Man kann in einer Funktion drei Zeichen von rechts herauslösen: Zusätze_Ohne_E: Rechts([Zusätze];3)
9.4.14 Lösung 4 Diese Aufgabe hat sicherlich keine Schwierigkeiten bereitet. Lässt man sich das Feld „Zusätze_Ohne_E“ anzeigen, dann kann ein weiteres Feld darauf Bezug nehmen. Anders als Excel ist Access weniger scharf bei der Trennung zwischen Text und Zahlen. Die Lösung könnte so aussehen: Zusätze_Geteilt_Durch_10: [Zusätze_Ohne_E]/10
316
/|VXQJHQ
Sandini Bib
9.4.15 Lösung 5 Zwar existiert in Access keine Funktion „Abrunden“ wie in Excel, aber die beiden Funktionen „Int“ und „Fix“. Beide schneiden die Nachkommastellen ab, ohne aufzurunden. Wollte man auf eine bestimmte Stelle nach oder vor dem Komma abrunden, so müsste man mit einer geschickten Multiplikation (und anschließender Division) mit diesen beiden Funktionen arbeiten. Hier sieht die Lösung wie folgt aus: Zusätze_Abrunden: Fix([Zusätze_Geteilt_Durch_10]) Zusätze_Abrunden: Int([Zusätze_Geteilt_Durch_10])
9.4.16 Lösung 6 Soll das Ergebnis eine Zahl bleiben, dann könnte man in den „Eigenschaften“ die Zahl formatieren: "C "0
Die Zahl „0“ steht hierbei allgemein für Zahlenzeichen, das „C“ für die „Einheit“ davor. Will man dagegen das Ergebnis als Text haben, dann könnte man es in einem neuen Feld verketten: Zusätze_Mit_C: "C " & [Zusätze_Abrunden]
9.4.17 Lösung 7 Mit Hilfe einer Wenn-Funktion können Teile ausgeblendet werden: Zusätze_Teil: Wenn([Wirkung]="unschädlich"; [Zusätze_Mit_C];"")
9.4.18 Lösung 8 Diese Funktionen können natürlich auch in einem Schritt zusammengefasst werden: Zusätze_Neu: Wenn([Wirkung]="unschädlich"; "C " & Int(Rechts([Zusätze];3)/10);"")
.DSLWHO
317
Sandini Bib
Abbildung 9.5: Sämtliche Funktionen in der Entwurfsansicht
Abbildung 9.6: Das Ergebnis der Abfrage
9.4.19 Lösung 9 In der Entwurfsansicht der Abfrage kann aus der Auswahlabfrage eine Tabellenerstellungsabfrage gemacht werden (Menü ABFRAGE). Diese wird entweder über das Symbol mit dem roten Ausrufezeichen gestartet („Ausführen“) oder durch Öffnen (oder Doppelklick) aus dem Datenbankfenster. Nun könnte die ursprüngliche Tabelle gelöscht werden.
9.5
Kapitel 5
Zu Excel
9.5.1 Lösung 1 Sub DateienPrüfung() Dim fTest1 As Boolean Dim fTest2 As Boolean Dim xlMappe As Workbook
318
/|VXQJHQ
Sandini Bib fTest1 = False: fTest2 = False For Each xlMappe In Application.Workbooks If LCase(xlMappe.Name) = "test01.xls" Then fTest1 = True End If If LCase(xlMappe.Name) = "test02.xls" Then fTest2 = True End If Next If fTest1 = False And fTest2 = False Then MsgBox "Die Dateien ""Test01.xls"" und " & _ """Test02.xls"" sind noch nicht offen." ElseIf fTest1 = False Then MsgBox "Datei ""Test01.xls"" ist noch nicht offen." ElseIf fTest2 = False Then MsgBox "Datei ""Test02.xls"" ist noch nicht offen." Else ' -- das eigentliche Programm End If End Sub
9.5.2 Lösung 2 Sub MonateErzeugen() Dim xlMappe As Workbook Dim i As Integer Set xlMappe = Application.ActiveWorkbook Application.DisplayAlerts = False For i = xlMappe.Worksheets.Count To 2 Step -1 xlMappe.Worksheets(i).Delete Next Application.DisplayAlerts = True xlMappe.Worksheets.Add Count:=11 For i = 1 To 12 xlMappe.Worksheets(i).Name = _ Format(DateSerial(2002, i, 1), "MMMM") Next End Sub
Natürlich kann man die elf neuen Blätter auch mit einer Schleife erzeugen. Genauso kann man die Beschriftung auch einzeln vornehmen. Leichter ist es allerdings, ein Datum zu erzeugen und dieses als Monat („MMMM“) zu formatieren.
.DSLWHO
319
Sandini Bib
9.5.3 Lösung 3 Sub SonstigesSuchen() Dim i As Integer For i = 1 To ActiveWorkbook.Worksheets.Count If ActiveWorkbook.Worksheets(i).Name = "Sonstiges" Then ActiveWorkbook.Worksheets(i).Activate Exit Sub End If Next MsgBox "Das Blatt ""Sonstiges"" existiert nicht." End Sub
9.5.4 Lösung 4 Sub FormelKopieren1() Dim strFormel As String strFormel = ActiveSheet.Range("C1").Formula ActiveSheet.Range("F7").Value = strFormel End Sub
Oder kürzer: Sub FormelKopieren2() ActiveSheet.Range("F7").Value = _ ActiveSheet.Range("C1").Formula End Sub
9.5.5 Lösung 5 Sub SummeErzeugen() Dim xlBlatt As Worksheet For Each xlBlatt In Worksheets xlBlatt.Range("C27").FormulaLocal = "=Summe(C1:C26)" Next End Sub
9.5.6 Lösung 6 Sub BezugAufVorherigesBlatt() Dim intZähler As Integer For intZähler = 2 To ActiveWorkbook.Sheets.Count ActiveWorkbook.Sheets(intZähler).Range("A1").Formula = _ "=" & ActiveWorkbook.Sheets(intZähler - 1).Name & "!R37" Next End Sub
Statt der Eigenschaft Formula kann auch FormulaLocal verwendet werden.
320
/|VXQJHQ
Sandini Bib
Zu Access
9.5.7 Lösung 7 Die Zeichenkette rs.Open "SELECT * FROM tab_Mitglieder WHERE " & _ "tMitJahresbeitrag > 170"
filtert beim Öffnen alle Mitglieder mit einem Jahresbeitrag, der höher ist als 170. Auf diesen Recordset wird ein Filter angewendet: strSQL = "tMitPlz LIKE '6*'" rs.Filter = strSQL
Er filtert alle Mitglieder, deren Adresse im Postleitzahlengebiet 6 liegt. Beim Durchlaufen aller Datensätze werden nur die herausgeholt, deren Zuname mit einem „M“ beginnt: If Left(rs.Fields("tMitZuname").Value, 1) = "M" Then strListe = strListe & vbCr & rs.Fields("tMitZuname").Value End If
Da die Filter hintereinander angewendet werden, bedeutet dies, dass zwischen ihnen eine UND-Verknüpfung vorliegt. Somit werden alle Mitglieder, deren Zuname mit „M“ beginnt, die in PLZ „6“ wohnen und mehr als 170 (Euro?) Jahresbeitrag bezahlen, herausgefiltert:
Abbildung 9.7: Die gefilterten Daten
9.5.8 Lösung 8 Die Prozedur bewirkt gar nichts. Wenn ein neues Element hinzugefügt werden soll, dann muss es mit der Methode Update beendet werden. AddNew alleine genügt nicht.
.DSLWHO
321
Sandini Bib
9.6
Kapitel 6
9.6.1 Lösung 1 Das dritte Dokument wird erzeugt. Die Kindknoten hängen alle am Wurzelknoten „Snow_White“, der durch das Objekt „x3“ erzeugt wird.
9.6.2 Lösung 2 Die anderen drei Dokumente kann man beispielsweise wie folgt erzeugen: Uebung01: ... Set x3 = xml.createElement("Snow_White") xml.appendChild x3 Set x4 = xml.createAttribute("Dwarfs") x4.Text = "sieben" x3.Attributes.setNamedItem x4 Set x5 = xml.createElement("Zwerg") x5.Text = "Dopey" x3.appendChild x5 Set x3 = xml.createElement("Zwerg") x3.Text = "Sleepy" x5.appendChild x3 Set x5 = xml.createElement("Zwerg") x5.Text = "Doc" x3.appendChild x5 Set x3 = xml.createElement("Zwerg") x3.Text = "Sneezy" x5.appendChild x3 Set x5 = xml.createElement("Zwerg") x5.Text = "Grumpy" x3.appendChild x5 Set x3 = xml.createElement("Zwerg") x3.Text = "Bashful" x5.appendChild x3 Set x5 = xml.createElement("Zwerg") x5.Text = "Happy" x3.appendChild x5 xml.Save "c:\Eigene Dateien\XML\Kapitel06\Uebung01.xml"
322
/|VXQJHQ
Sandini Bib
Uebung02: ... i = 1 Set x5 = xml.createElement("Zwerg" x5.Text = "Dopey" x3.appendChild x5 i = i + 1 Set x5 = xml.createElement("Zwerg" x5.Text = "Sleepy" x3.appendChild x5 i = i + 1 Set x5 = xml.createElement("Zwerg" x5.Text = "Doc" x3.appendChild x5 i = i + 1 Set x5 = xml.createElement("Zwerg" x5.Text = "Sneezy" x3.appendChild x5 i = i + 1 Set x5 = xml.createElement("Zwerg" x5.Text = "Grumpy" x3.appendChild x5 i = i + 1 Set x5 = xml.createElement("Zwerg" x5.Text = "Bashful" x3.appendChild x5 i = i + 1 Set x5 = xml.createElement("Zwerg" x5.Text = "Happy" x3.appendChild x5
Uebung04: ... Set x5 = xml.createElement("Dwarfs") x3.appendChild x5 i = 1 x3.setAttribute "Zwerg" & i, "Dopey": i = i + 1 x3.setAttribute "Zwerg" & i, "Sleepy": i = i + 1 x3.setAttribute "Zwerg" & i, "Doc": i = i + 1 x3.setAttribute "Zwerg" & i, "Sneezy": i = i + 1 x3.setAttribute "Zwerg" & i, "Grumpy": i = i + 1 x3.setAttribute "Zwerg" & i, "Bashful": i = i + 1 x3.setAttribute "Zwerg" & i, "Happy": i = i + 1 xml.Save "c:\Eigene Dateien\XML\Kapitel06\Uebung04.xml"
.DSLWHO
323
Sandini Bib
9.6.3 Lösung 3 Die Schleife For Each x3 In xml.documentElement.childNodes Next
durchläuft alle Kindknoten des Wurzelelements (documentElement). Weitere Unterelemente werden dabei nicht berücksichtigt. Die Eigenschaft Text sammelt die Inhalte, nicht die Namen, ein. Auf die Namen könnte man mit der Eigenschaft BaseName zugreifen. Deshalb ist die fünfte Antwort korrekt.
9.7
Kapitel 7
9.7.1 Lösung 1 Das Dokument wurde nicht gespeichert. Deshalb wird keine der Veränderungen übernommen. Das erste Dokument ist das Ergebnis der „Transformation“.
9.7.2 Lösung 2 Das zweite Dokument erhält man, indem man das erste speichert: xml.Save "c:\Eigene Dateien\XML\Kapitel07\Uebung02.xml"
Beim dritten Dokument wurde nicht beachtet, dass die Zählung bei 0 beginnt. Es wird durch folgenden Code erzeugt: ... With xml.documentElement .removeChild .FirstChild .replaceChild .childNodes(3), .childNodes(1) Set x0 = .LastChild.CloneNode(True) .appendChild x0 .insertBefore x0, .FirstChild End With xml.Save "c:\Eigene Dateien\XML\Kapitel07\Uebung03.xml"
Das vierte vertauscht die beiden Parameter newChild und oldChild bei der Methode replaceChild, beziehungsweise newChild und refChild bei insertBefore:
324
/|VXQJHQ
Sandini Bib ... With xml.documentElement .removeChild .FirstChild .replaceChild .childNodes(2), .childNodes(4) Set x0 = .LastChild.CloneNode(True) .appendChild x0 .insertBefore .FirstChild, x0 End With xml.Save "c:\Eigene Dateien\XML\Kapitel07\Uebung04.xml"
9.7.3 Lösung 3 Da das Objekt x3 als Attribut deklariert wurde, tritt ein Fehler beim Beginn der Schleife auf: For Each x3 In xml.documentElement.childNodes
Die Fehlermeldung lautet: „Typen unverträglich“, da x3 kein Element der Sammlung der Knoten ist.
9.8
Kapitel 8
9.8.1 Lösung 1 Wie unschwer zu erkennen ist, werden zwei Dateien erzeugt. Das XMLDokument sieht wie folgt aus: Spiderman Batman Superman
Das XSL-Dokument „XSLUebung05.xsl“ hat folgende Gestalt:
Helden wie wir
.DSLWHO
325
Sandini Bib
Und im Browser sieht es folgendermaßen aus:
Abbildung 9.8: Das per VBA erzeugte XML-Dokument
326
/|VXQJHQ
Sandini Bib
A
Ausblick
le
n e rn
Vor mir im Regal stehen viele XML-Bücher. Manche von ihnen sind über 1.000 Seiten dick. Ein Buchhändler im Internet zeigt 86 Bücher über XML. Tendenz steigend. Das vorliegende hat nur einige XMLAspekte beschrieben – es gibt noch viel zu sagen. Über jedes einzelne der Kapitel kann noch mehr geschrieben werden. Das bereits Geschriebene kann vertieft werden.
A.1 XML-Dokumente In Kapitel 1 und 2 sind die wichtigsten Begriffe, Definitionen und Regeln der XML-Spezifikation aufgelistet. Was fehlt? XML-Puristen mögen mir vorwerfen, dass die Themen XPath und XLink nicht erwähnt wurden. Das ist richtig. Ich habe darüber nachgedacht und beschlossen, dass diese beiden Themen für die Dinge, die ich zeigen wollte, nicht relevant sind. Zugegeben: XLink habe ich im Kapitel 3.3.12 erwähnt, ohne es zu benennen. Über Links gäbe es noch eine Menge Dinge zu erzählen. Für den Anfang genügt das hier Beschriebene.
A.2 Darstellung von XML Auch die CSS-Dateien sind komplexer als in Kapitel 3 beschrieben. Das W3C hat CSS1 und CSS2-Spezifikationen herausgebracht, auf deren Unterschiede hier nicht eingegangen wird. Mit CSS können auch lange Dokumente formatiert werden – dieser Punkt wurde in meinem Buch ausgeklammert. Die Informationen finden Sie alle im vorliegenden Buch – der interessierte Leser kann sich nun überlegen, wie man ein mit Formatvorlagen formatiertes Worddokument in ein XML-Dokument verwandelt, an welches die Formate in Form einer CSS-Datei gebunden sind.
;0/'RNXPHQWH
327
Sandini Bib
Ebenso habe ich einige Kleinigkeiten von XSL, die mir nicht so sehr wichtig erschienen, stillschweigend übergangen. Inzwischen sind schon die ersten Bücher nur über XSL auf dem Markt erschienen. Ich bin der Meinung, dass der hier vorgestellte Stoff fürs Erste genügt. Damit haben Sie eine gute Grundlage für die Gestaltung von XML-Dokumenten.
A.3 Excel, Access und VBA Über Excel, Access und VBA gibt es viele Bücher, in denen mehr steht, als hier beschrieben wurde. Ich habe mich in Kapitel 4 und 5 auf das Wesentliche beschränkt, das nötig ist, wenn man Daten aus Excel oder Access exportieren möchte. Ich gehe davon aus, dass die Daten bereits vorliegen und nicht neu abgetippt werden. Vielleicht geben Sie bei einer kleinen Liste den XML-Code ein, aber bei mehreren Hundert Datensätzen wird dies mühsam. In Kapitel 6 und 7 wurde gezeigt, wie man mit Hilfe von VBA Daten in eine XML-Datei schreiben kann. Dabei können problemlos Elemente, Attribute und Inhalte erzeugt werden. Umgekehrt kann ein XML-Dokument per VBA durchlaufen und die darin befindlichen Daten in eine Excel-Tabelle (oder eine Datenbank) geschrieben werden. Man kann wohlgeformte Dokumente auf ihre Gültigkeit überprüfen – und im Falle eines Fehlers – diesen bestimmen lassen. Leider ist es nicht möglich, eine DTD mit VBA zu erzeugen. Allerdings kann eine gültige Datei mit einer (internen oder externen DTD) validiert werden. So kann sichergestellt werden, dass nachträgliche Veränderungen am Dokument noch immer der Dokumentdefinition entsprechen. Deshalb wurde in Kapitel 2 erklärt, was ein Schema ist und wie es aufgebaut ist. Da Schema-Dateien den XML-Regeln folgen, können sie per Programmierung erzeugt werden. Es ist anzunehmen, dass sie in Zukunft einen breiteren Raum einnehmen werden als bisher.
A.4 Weitere Einsatzbereiche von XML XML nimmt einen immer wichtigeren Stellenwert im Bereich Daten und Dokumente ein. An dieser Stelle sei nur auf einige spezielle Einsatzbereiche verwiesen, die hier nicht beschrieben wurden. Mit der Chemical Markup Language (CML) können Strukturen komplexer Moleküle dargestellt werden. Die Mathematical Markup Language (MathML) ermöglicht die Darstellung von mathematischen Gleichungen im Internet. Die Synchronized Multimedia Integration Language (SMIL) bindet mehrere Multimedia-Dateien in ein Dokument ein, also Videos, Bilder und Sound.
328
$XVEOLFN
Sandini Bib
Auch einige Grafikformate basieren bereits auf XML. An erster Stelle wäre hier die Vector Markup Language (VML) zu nennen, aber auch die Scalable Vector Graphics (SVG) und Precision Graphics Markup Language (PGML). Und schließlich die Wireless Markup Language (WML), auf der das Wireless Application Protocol (WAP) basiert. Sie sehen, dass die Anwendungsgebiete vielfältig sind. Und ich bin sicher, dass in naher Zukunft XML eine noch größere Rolle spielen wird als es bereits der Fall ist. Es bleibt ein spannendes Austauschformat zwischen allen Arten von Texten und Daten.