Datenzugriff

Lesen und Schreiben von Textdateien gehören zum Pflichtprogramm einer Skriptsprache, der Umgang mit XML-Dokumenten und Datenbanken hingegen zur Kür. Die PowerShell bietet hier elegante Möglichkeiten.

In Pocket speichern vorlesen Druckansicht
Lesezeit: 7 Min.
Von
  • Dr. Holger Schwichtenberg
Inhaltsverzeichnis

In der vierten und letzten Folge des PowerShell-Tutorials geht es um schreibenden und lesenden externen Datenzugriff, zum einen auf XML-Dateien, zum anderen auf relationale Datenbanken.

Um XML-Dokumente auszuwerten, bietet die PowerShell eine komfortable Vorgehensweise, da der Administrator die XML-Elementnamen wie Attribute eines .Net-Objekts ansprechen kann. Wenn die Variable $doc das in Listing 1 gezeigte XML-Dokument enthält, liefert der Ausdruck $doc.Websites.Website die Menge von XML-Knoten, die <Website> heißen. Die Verwendung der Attribute und Methoden des XML Document Object Model (DOM) ist dadurch elegant gekapselt.

Mehr Infos

Listing 1: Zentrale Beispieldatei

<?xml version="1.0" encoding="utf-8"?>
<Websites>
<Website ID="1">
<URL>www.ix.de</URL>
<Beschreibung>Website des iX-Magazins</Beschreibung>
</Website>
<Website ID="2">
<URL>www.IT-Visions.de</URL>
<Beschreibung>Websites des Autors dieses Beitrags</Beschreibung>
</Website>
<Website ID="3">
<URL>www.dotnetframework.de</URL>
<Beschreibung>Community-Site für .NET-Entwickler</Beschreibung>
</Website>
<Website ID="4">
<URL>www.Windows-Scripting.de</URL>
<Beschreibung>Community-Site für WSH- und PowerShell-Entwickler</Beschreibung>
</Website>
<Website ID="5">
<URL>www.powershell-doktor.de</URL>
<Beschreibung>Community-Site zur Microsoft PowerShell</Beschreibung>
</Website>
</Websites>

Die Konsole bietet für die XML-Elemente Tabulatorvervollständigung, PowerShell Plus (siehe gleichnamigen Kasten) sogar echte IntelliSense. Diese Komfortfunktion erhält nicht mal der .Net-Entwickler im Visual Studio, denn die Code-Editoren des VS können nicht in die XML-Dokumente hineinsehen; Konsolen haben den Vorteil, dass sie die Befehle zeilenweise ausführen. Im Code-Editor der PowerShell Plus funktioniert IntelliSense auch im Skriptcode, wenn man das Skript einmal im Debugger hat laufen lassen.

Die Kapselung der XML-Dokumente in der PowerShell erfolgt durch den sogenannten PowerShell-XML-Objektadapter. Um den Adapter nutzen zu können, muss die PowerShell wissen, welche Variablen ein XML-Dokument enthält. Daher ist die Typkonvertierung mit [XML] in der ersten Zeile der folgenden Abfrage wichtig. Die Variable enthält dadurch eine Instanz der .Net-Klasse System.Xml.XmlDocument. Mit den Anweisungen

$doc = [XML] (Get-Content -Path h:\ix\xml\websites.xml)
$Sites = $doc.Websites.Website
$Sites | select URL, Beschreibung

kann man eine Liste der URLs und der Beschreibungen aus dem XML-Dokument von Listing 1 ausgeben.

Zur Suche in XML-Dokumenten mithilfe von XPath [2] unterstützt die Klasse XmlDocument die Methoden SelectNodes() und SelectSingleNode(). In den PowerShell Community Extensions (PSCX) [3] gibt es das Commandlet Select-Xml. SelectNodes() und SelectSingleNode() liefern Instanzen der Klassen System.Xml.XmlElement und System.Xml.XmlAttribute. Select-Xml hingegen liefert Instanzen von MS.Internal.Xml.Cache.XPathDocumentNavigator. Die Ausgabe ist daher unterschiedlich. Um bei beiden Befehlen zur gleichen Ausgabe zu kommen, muss man das Ergebnis von Select-Xml an Select-ObjectInnerXml senden.

Für den Zugriff auf das <URL>-Element des <Website>-Elements mit dem Attributwert 3 im Attribut ID schreibt der PowerShell-Nutzer entweder

$doc = [XML] (Get-Content -Path h:\ix\xml\websites.xml)
$doc.SelectSingleNode ("//Website[@ID=3]/URL")

oder

select-Xml h:\ix\xml\websites.xml -XPath "//Website[@ID=3]/URL" | select innerxml

Select-Xml hat dabei den Vorteil der einfachen Unterstützung für XML-Namensräume.

Um aus einer Visual-Studio-Projektdatei die Namen aller referenzierten C#-Quellcodedateien auszulesen, wäre der folgende Befehl hilfreich:

Select-Xml "H:\ix\_Eigene Commandlets\PowerShell_Commandlet_Library\PowerShell_Commandlet_Library.csproj "-Namespace ,dns=http://schemas.microsoft.com/developer/msbuild/2003' -XPath "//dns:Compile/@Include"

Dabei muss der Anwender auf den entsprechenden Namensraum des Kommandozeilenwerkzeugs MSBuild Bezug nehmen, das für die Übersetzung der Visual-Studio-Projekte ab der Version 2005 zuständig ist.

Den Versuch, ein nicht gültiges XML-Dokument (in dem beispielsweise ein schließender Tag fehlt) in den Typ [XML] zu konvertieren, quittiert die PowerShell mit einem Fehler. Mit dem Commandlet Test-Xml (aus den PSCX) kann man vorher prüfen, ob ein Dokument gültig ist. Es liefert True oder False, wenn man den Pfad zu einem XML-Dokument oder ein XmlDocument-Objekt in der Pipeline übergibt.

$doc = [XML] (Get-Content -Path h:\ix\xml\websites.xml)
$doc | Test-Xml

Test-Xml prüft im Standard aber nur die Gültigkeit. Optional ist eine Validierung gegen ein oder mehrere XML-Schemata möglich. Hierbei ist nach -SchemaPath der Pfad zu der XML-Schema-Datei (.xsd) anzugeben.

Test-Xml
h:\ix\xml\websites.xml -SchemaPath h:\ix\xml\websites.xsd

Alternativ kann der Anwender dort ein Array mit mehreren Pfaden nennen.

Formatierte Ausgabe des XML-Dokuments in der PowerShell (Abb. 1).

XML-Dokumente müssen nicht formatiert sein, Einrückungen der XML-Elemente entsprechend der Ebene sind nicht notwendig. In den PSCX gibt es das Commandlet Format-Xml, das nicht formatierte XML-Dokumente formatiert ausgibt beziehungsweise die Formatierung der Ausgabe anpasst. Der folgende Befehl liefert eine formatierte Ausgabe eines XML-Dokuments, bei der jede Ebene mit einem Punkt und vier Leerzeichen eingerückt wird (siehe Abbildung 1).

Format-Xml
h:\ix\xml\websites.xml -IndentString ". "
Mehr Infos

Listing 2: Ergänzen einer XML-Datei

"Vorher"
$doc = [xml] (Get-Content -Path h:\ix\xml\websites.xml)
$doc.Websites.Website | select URL,Beschreibung
"Nachher"
$site = $doc.CreateElement("Website")
$url = $doc.CreateElement("URL")
$url.set_Innertext("www.powershell-doktor.de")
$beschreibung = $doc.CreateElement("Beschreibung")
$beschreibung.set_Innertext("Community-Website zur PowerShell")
$site.AppendChild($url)
$site.AppendChild($Beschreibung)
$doc.Websites.AppendChild($site)
$doc.Websites.Website | select URL,Beschreibung
"Dokument speichern"
$doc.Save("h:\ix\buch\websites_neu.xml")

Das Skript in Listing 2 ergänzt einen Eintrag in einer XML-Datei unter Verwendung der Methoden CreateElement() und AppendChild(). Es zeigt aber auch, dass es Ecken in der PowerShell gibt, die etwas komplizierter sein können. Weil die Unterelemente eines XML-Knotens als Attribute der .Net-Klasse bereitstehen, sind - zur Vermeidung von Namenskonflikten - die Attribute der Metaklasse System.Xml.Node (bzw. davon abgeleiteter Klassen) versteckt. Diese Attribute sind nur über ihre Getter- und Setter-Methoden verfügbar. Folglich kann der Nutzer mit dem PowerShell-Skript den Inhalt eines Knotens nicht über

$knoten.Innertext = "xyz"

setzen, sondern muss etwas umständlicher

$knoten._set_Innertext("xyz")

aufrufen.

Die PowerShell verwendet mit CLIXML ein eigenes XML-Format, mit dem sie die Objekt-Pipeline in XML-Form (durch Export-CliXml) serialisieren kann. Der folgende Befehl speichert die Objektliste der laufenden Systemdienste.

Get-Service
| Where-Object {$_.status -eq "running"}
| Export-CliXml j:\demo\dokumente\dienste.xml

Das Gegenstück zur Wiederherstellung der Pipeline ist

Import-CliXml
j:\demo\dokumente\dienste.xml | Get-Member

Nach der Deserialisierung der Objekte kann man alle Attribute der Objekte wieder verwenden; man kann aber nicht mehr Methoden der Objekte aufrufen, da durch die Deserialisierung ein generisches PowerShell-Objekt entstanden ist, das die Methoden der Ursprungsklasse nicht mehr besitzt.

Für die Anwendung des W3C-Standards XSLT [4] steht in den PSCX das Commandlet Convert-Xml zur Verfügung. Alternativ kann man die .Net-Klasse System.Xml.Xsl.XslCompiledTransform verwenden.

Mehr Infos

Listing 3: XSLT-Datei für die Transformation

<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- Transformation -->
<xsl:template match="Websites">
<HTML>
<body>
<h2>Websites</h2>
<ul>
<xsl:for-each select="/Websites/Website">
<xsl:value-of select='Beschreibung'/>
<br>
URL: <xsl:value-of select="URL"/>
</br>
</xsl:for-each>
</ul>
<hr></hr>
Konvertiert aus XML
</body>
</HTML>
</xsl:template>
</xsl:stylesheet>

Diese HTML-Datei hat die PowerShell aus der XML-Datei generiert (Abb. 2).

Das nächste Beispiel zeigt, wie man die XML-Datei Websites.xml mithilfe der in Listing 3 gezeigten XSLT-Datei in eine XHTML-Datei konvertieren kann. Das Ergebnis speichert das Skript als Websites.html, die Darstellung im Browser zeigt schließlich Abbildung 2.

Convert-Xml h:\ix\xml\websites.xml -XsltPath H:\DEV\ITVisions_PowerShell_CommandletLibrary\CommandletLibrary\Daten\WebsitesToHTML.xslt | Set-content h:\ix\xml\websites.html

Außer der XML-Verarbeitung zeigt der Artikel im Printmedium den Zugriff auf relationale Datenbanken. Den vollständigen Artikel finden Sie in der aktuellen Printausgabe.


[1] Holger Schwichtenberg; ADO.Net 2.0 und SQL-Server 2005; Ziel nicht erreicht; iX 5/05, S. 124

Mehr Infos

(wm)