Hey, Scripting Guy!Berühmte letzte Worte

Die Scripting Guys von Microsoft

Der berühmte griechische Philosoph Sokrates († 399 v. Chr., kurz vor der Geburtsstunde des Skripting Editor) ist wohl am bekanntesten für seine Aussage: „Ein ungeprüftes Leben ist nicht lebenswert.“ Die meisten Menschen kennen diesen Spruch. Aber erstaunlicherweise haben die Scripting Guys vor kurzem entdeckt, dass Sokrates nie gesagt, dass ein ungeprüftes Leben nicht lebenswert. Wie sich herausstellt, war dies eine Fehlübersetzung eines mittelalterlichen Schreibers vor Hunderten von Jahren. Was Sokrates tatsächlich gesagt hatte, war: „Eine ungeprüfte XML-Datei ist nicht habenswert.“

Endlich gibt es einen philosophischen Spruch, der tatsächlich Sinn ergibt! XML wird heutzutage immer beliebter. Eine schnelle Suche mit einem Testcomputer der Scripting Guys liefert mehr als 500 XML-Dateien, die von verschiedenen Systemen oder Anwendungen verwendet werden.

Natürlich sind auch viele sehr wichtige Daten jetzt im XML-Format gespeichert. Das ist auch gut so. Es sei denn, die Daten sind ungeprüft. Leider ist das oft der Fall, manchmal sogar aus dem folgenden einfachen Grund: Viele Menschen wissen nicht, wie sie eine XML-Datei überhaupt untersuchen können. Insbesondere wissen sie nicht, wie sie diese XML-Datei abfragen und durchsuchen sollen.

Hinweis Falls Sie sich nicht sonderlich gut mit griechischen Philosophen auskennen: Sokrates war ein großer Befürworter der Methode, durch das Land zu ziehen und über Dinge zu sprechen, aber er hatte nicht allzu viel dafür übrig, tatsächlich etwas zu tun, weshalb seine Ehefrau Xanthippe ihn als „nutzlosen Müßiggänger“ bezeichnete. So ist gleich erkennbar, warum die Scripting Guys ein Faible für Sokrates haben.

Einige Leser werden jetzt natürlich denken: „He, einen Moment mal: Haben die Scripting Guys das Durchsuchen von XML-Dateien nicht schon in einem älteren Artikel behandelt („Autos nachjagen ... und XML“, technet.microsoft.com/magazine/cc162506)? Warum bringen sie dieses Thema jetzt wieder zur Sprache? Sind die Scripting Guys so faul, dass sie denselben Artikel glatt noch einmal schreiben?“

Ob Sie es glauben oder nicht: wir sind sogar noch fauler. Aber das ist nicht der wichtigste Grund, warum wir dieses Thema noch einmal anschneiden. In unserem früheren Artikel haben wir die Arbeit mit einer XML-Datei besprochen, die wie der Code in Abbildung 1 strukturiert war. Jeder einzelne Eigenschaftswert stellte dabei einen einzigen Knoten in der Datei dar.

Abbildung 1 XML-Datei nur mit Knoten

<?xml version='1.0'?> 
  <INVENTORY> 
    <COMPUTER>
      <os>Windows XP</os>
      <department>Human Resources
      </department>
      <name>atl-ws-001</name>
    </COMPUTER> 
    <COMPUTER>
      <os>Windows XP</os>
      <department>Finance</department>
      <name>atl-ws-002</name>
    </COMPUTER> 
    <COMPUTER>
      <os>Windows Vista</os>
      <department>Finance</department>
      <name>atl-ws-003</name>
    </COMPUTER> 
  </INVENTORY>

Das ist toll, aber XML-Dateien können – wie viele Menschen sofort schrieben und anmerkten – nicht nur auf diese Weise strukturiert werden. Im vorhergehenden Beispiel besaß jeder Computer in der Datei einen eigenen Knoten, und jeder dieser Knoten umfasste wiederum einige untergeordnete Knoten (Betriebssystem, Abteilung, Name). Es ist jedoch auch möglich, eine XML-Datei zu erstellen, in der die einzelnen Knoten keine untergeordneten Knoten besitzen, sondern stattdessen zusätzliche Eigenschaftswerte als Attribute konfiguriert sind. So wie die Datei in Abbildung 2.

Abbildung 2 XML-Datei mit Attributen

<?xml version='1.0'?> 
  <HARDWARE> 
      <COMPUTER os="Windows XP" department="Human Resources">atl-ws-001</COMPUTER> 
      <COMPUTER os="Windows XP" department="Finance">atl-ws-002</COMPUTER> 
      <COMPUTER os="Windows Server 2003" department="IT">atl-fs-003</COMPUTER> 
      <COMPUTER os="Windows Vista" department="IT">atl-ws-004</COMPUTER> 
      <COMPUTER os="Windows Vista" department="Human Resources">atl-ws-005</COMPUTER> 
      <COMPUTER os="Windows Vista" department="Finance">atl-ws-006</COMPUTER> 
      <COMPUTER os="Windows XP" department="Sales">atl-ws-007</COMPUTER> 
      <COMPUTER os="Windows Server 2008" department="IT">atl-fs-008</COMPUTER> 
      <COMPUTER os="Windows XP" department="Human Resources">atl-ws-009</COMPUTER> 
      <COMPUTER os="Windows Vista" department="Sales">atl-ws-010</COMPUTER> 
  </HARDWARE>

Ist das ein Problem? Durchaus. Das Skript, das wir Ihnen vor längerer Zeit in einem uralten Artikel präsentiert hatten, funktioniert nicht mit einer XML-Datei, die wie die in Abbildung 2 gezeigte Datei strukturiert ist. Es geht einfach nicht. Das ist definitiv ein Problem, weil viele von Ihnen in der Lage sein müssen, diese Art von XML-Datei zu lesen.

Ihr Wunsch war uns Befehl. Nun ja, Ihr Wunsch und eineinhalb Jahre Zeit, bis wir das Problem endlich angingen.

Bevor wir weiter ins Detail gehen, wollen wir unsere XML-Datei näher betrachten. In diesem Fall haben wir eine Datei mit dem primären Knoten namens HARDWARE. Jeder einzelne Computer ist ein untergeordneter Knoten von HARDWARE. Außerdem besitzen diese Knoten je zwei Attribute: „os“ (hier wird der Name des Betriebssystems gespeichert, das auf dem Computer installiert ist) und „department“ (der Name der Abteilung, zu der der Computer gehört).

Angenommen, wir möchten eine Liste aller Computer abrufen, auf denen Windows XP ausgeführt wird. Können wir das mit einem Skript erledigen? Selbstverständlich.

Set xmlDoc = _
  CreateObject("Microsoft.XMLDOM")

xmlDoc.Async = "False"
xmlDoc.Load("HARDWARE.xml")

Set colNodes=xmlDoc.selectNodes _
  ("//HARDWARE/COMPUTER[@os='Windows XP']")

For Each objNode in colNodes
  Wscript.Echo objNode.Text 
Next

Sehen wir einmal, was wir hier haben. Zunächst erstellen wir eine Instanz des Microsoft.XMLDOM-Objekts. Wie der Name schon sagt, gibt uns dieses Objekt die Möglichkeit, mit XML-Dateien zu arbeiten. Sobald wir das Objekt erstellt haben, setzen wir die Async-Eigenschaft auf „False“. Damit teilen wir dem Skript mit, dass wir das Dokument synchron statt asynchron laden möchten. Warum sollte das für das Skript wichtig sein? Um ehrlich zu sein, macht das dem Skript selbst nichts aus. Skripts sind schließlich unbelebte Objekte (wie die Scripting Guys).

Aber Sie sollten das wichtig nehmen. Wenn wir das Dokument asynchron geladen hätten, würde das Skript auf jeden Fall weiter abgearbeitet, selbst wenn das Dokument nicht vollständig geladen wird. Das ist nicht gut. Stellen Sie sich nur die Probleme vor, die auftreten könnten, wenn Sie versuchen, eine Aufgabe mit einem Dokument durchzuführen, das noch gar nicht vorhanden ist. Das synchrone Laden einer XML-Datei sorgt dafür, dass die Datei vollständig geladen wird, bevor das Skript weiter abläuft.

Welch überraschender Zufall: Jetzt sind wir an dem Punkt angelangt, an dem wir unsere XML-Datei laden. Hierzu rufen wir die Load-Methode auf, und wir öffnen die Datei „C:\Scripts\HARDWARE.xml“, die uns zur folgenden Zeile im Code bringt:

Set colNodes=xmlDoc.selectNodes _
  ("//HARDWARE/COMPUTER[@os='Windows XP']")

Hier fragen wir die XML-Datei mit der selectNodes-Methode ab, und wir teilen dem Skript mit, welche XML-Knoten abgerufen werden sollen. Beachten Sie die Syntax, die zugegebenermaßen etwas ungewöhnlich ist.

Zunächst müssen wir den Pfad innerhalb der XML-Datei angeben. Unsere XML-Datei besitzt einen Knoten mit der Bezeichnung HARDWARE in der höchsten Ebene, gefolgt von einer Reihe von Knoten zweiter Ebene mit der Bezeichnung COMPUTER. Jeder dieser Knoten in der zweiten Ebene steht für einen einzelnen Datensatz in unserer XML-Datei. Darum beginnt unsere selectNodes-Abfrage wie folgt:

//HARDWARE/COMPUTER

An diesem Punkt treffen wir auf die folgende Konstruktion:

[@os='Windows XP']

Dieser Teil der Abfrage ähnelt einer WHERE-Klausel in einer SQL-Standardabfrage. Mit SQL würden wir eine ähnlich aufgebaute Datenbankabfrage verwenden, um eine Liste aller Computer abzurufen, auf denen das Windows XP-Betriebssystem ausgeführt wird:

SELECT Name FROM Hardware 
    WHERE OS = 'Windows XP'

Mit der selectNodes-Methode gehen wir ähnlich vor: Wir verlangen eine Liste aller Computer, bei denen das os-Attribut (@os) den Wert „Windows XP“ aufweist. Um anzuzeigen, dass dies eine WHERE-Klausel ist, schließen wir die gesamte Klausel in eckige Klammern ein.

Hinweis Dieser Abfragetyp wird als XPath-Abfrage bezeichnet. Weitere Informationen zu XPath finden Sie unter msdn.microsoft.com/library/ms256115.aspx.

Wie schon gesagt: Es ist zwar ein bisschen merkwürdig, aber es funktioniert. Was ist, wenn wir eine Liste aller Computer abrufen möchten, die zur Finanzabteilung gehören? Das ist keine große Sache. Unser Aufruf von selectNodes wäre dann wie folgt aufgebaut:

Set colNodes=xmlDoc.selectNodes _
  ("//HARDWARE/COMPUTER" & _
   "[@department='Finance']")

Wieder ist unsere WHERE-Klausel in eckige Klammern eingeschlossen, wir leiten den Attributnamen („department“) mit dem At-Zeichen (@) ein, und dann geben wir den Wert an, für den wir uns interessieren.

Ganz einfach, oder?

Sobald wir die selectNodes-Methode aufgerufen haben, können wir die Computernamen zurückgeben, indem wir einfach die Reihe der zurückgegebenen Werte per Schleife durchgehen und dann die Text-Eigenschaft zurückgeben, so wie hier:

For Each objNode in colNodes
  Wscript.Echo objNode.Text 
Next

Welche Art von Informationen können wir dann erwarten? Ehrlich gesagt, können wir zu Recht mit den folgenden Informationen rechnen:

atl-ws-001
atl-ws-002
atl-ws-007
atl-ws-009

Anders gesagt, werden die Namen aller Computer zurückgegeben, auf denen immer noch Windows XP ausgeführt wird.

Im Übrigen können Sie nicht nur den Computernamen zurückgeben lassen. Der Computername ist der „Standardwert“ für jeden Knoten, und genau diesen Wert bekommen wir zurück, wenn wir auf die Text-Eigenschaft verweisen.

Alternativ könnten wir die gesuchten Attributwerte exakt angeben. Betrachten Sie beispielsweise diese überarbeitete For Each-Schleife:

For Each objNode in colNodes
    Wscript.Echo objNode.Text 
    Wscript.Echo objNode.Attributes. _
      getNamedItem("department").Text
    Wscript.Echo
Next

Wie Sie sehen, geben wir in dieser Schleife immer noch den Wert der Text-Eigenschaft zurück, aber wir haben auch die folgende Codezeile eingefügt:

Wscript.Echo objNode.Attributes. _
  getNamedItem("department").Text

In diesem Fall rufen wir den Wert des department-Attributs mit der getNamedItem-Methode ab, und anschließend geben wir die Text-Eigenschaft für dieses Attribut zurück. So können wir festlegen, welche Attributwerte wir auf dem Bildschirm anzeigen lassen möchten (und welche nicht). (Beachten Sie auch, dass wir den Wscript.Echo-Befehl eingefügt haben, mit dem eine Leerzeile zwischen den Datensätzen eingefügt wird.) Wenn wir dieses Skript ausführen, sollten wir das folgende Ergebnis erhalten:

atl-ws-001
Human Resources

atl-ws-002
Finance

atl-ws-007
Sales

atl-ws-009
Human Resources

Und ja, Sie können auch komplexere Abfragen schreiben, wenn Sie möchten. Angenommen, Sie möchten beispielsweise eine Liste aller Computer abrufen, auf denen entweder Windows XP oder Windows Vista ausgeführt wird. In SQL würden Sie hierzu eine OR-Abfrage schreiben. Merken Sie was? Sie gehen hier genau so vor wie beim Abfragen einer XML-Datei:

Set colNodes=xmlDoc.selectNodes _
    ("//HARDWARE/COMPUTER" & _
     "[@os='Windows XP' or " & _
     "@os='Windows Vista']")

Sehen Sie, was hier geschehen ist? Innerhalb von nur zwei eckigen Klammern haben wir zwei Kriterien angegeben: @os='Windows XP' or @os='Windows Vista'. Mehr müssen Sie nicht tun, und schon erhalten Sie einen Bericht wie in Abbildung 3.

Abbildung 3 Ergebnis der OR-Abfrage

atl-ws-001
Human Resources

atl-ws-002
Finance

atl-ws-004
IT

atl-ws-005
Human Resources

atl-ws-006
Finance

atl-ws-007
Sales

atl-ws-009
Human Resources

atl-ws-010
Sales

Wenn Sie die XML-Datei überprüfen, werden Sie feststellen, dass die Computer, auf denen Windows XP oder Windows Vista ausgeführt wird, in der Ausgabe aufgeführt werden, Computer mit Windows Server 2003 oder Windows Server 2008 dagegen nicht. Das sollen sie ja auch nicht.

Sie haben eine Frage? Sie möchten eine stärker eingeschränkte Abfrage schreiben, mit der nur Computer zurückgegeben werden, auf denen Windows XP ausgeführt wird und die zur Finanzabteilung gehören? Wie Sie sich vielleicht schon denken können, ist das ganz einfach: Hierzu müssen Sie nur eine AND-Abfrage wie die folgende schreiben:

Set colNodes=xmlDoc.selectNodes _
    ("//HARDWARE/COMPUTER" & _
     "[@os='Windows XP' and " & _
     "@department='Finance']")

Damit wird das folgende Dataset zurückgegeben:

atl-ws-002
Finance

Warum nur ein Element in unserem Bericht? Ganz genau: weil die Finanzabteilung nur einen einzigen Computer besitzt, auf dem Windows XP ausgeführt wird.

Wir hoffen, dass wir Ihnen helfen konnten, diese spezielle Art von XML-Dateien zu bewältigen. Wenn irgendwann einmal eine weitere Art von XLM-Dateien auftauchen sollte, dann viel Glück damit.

Wir haben den Artikel dieses Monats mit einem berühmten Ausspruch begonnen, und deshalb finden wir es passend, den Artikel auch in dieser Weise zu beenden. Wir schließen also mit den Worten von Kaiser Wilhelm II., die wir Scripting Guys in allen Ehren halten: „Ich verzichte hierdurch für alle Zukunft auf die Rechte an der Krone Preußens und die damit verbundenen Rechte an der deutschen Kaiserkrone.“

Der Scripting Perplexer von Dr. Scripto

Die monatliche Herausforderung, die nicht nur Ihr Talent zum Rätsellösen testet, sondern auch Ihre Skriptingfähigkeiten.

Oktober 2008: VBScript-Rätsel

Tragen Sie in jede Zeile die Bezeichnung einer VBScript-Funktion ein. Die Buchstaben in den blauen Quadraten bilden die Bezeichnung einer weiteren Funktion.

fig10.gif

Antwort:

Der Scripting Perplexer von Dr. Scripto

Antwort: Oktober 2008: VBScript-Rätsel

puzzle_answer.gif

Die Scripting Guys – Greg Stemp und Jean Ross – arbeiten für Microsoft.