- Zugriff auf XML-Daten in PowerShell
- Zugriff auf XML mit XPath.
- Zugriff auf XML als Objekte.
- Vergleich von XPath- und Objektansätzen
- Ändern oder Erstellen von XML-Daten
- Hinzufügen von XML-Daten
- Verwenden von XML für die Objektserialisierung
Einführung
Wie bei jeder Hochsprache ist the data
das Herzstück von PowerShell. Im Fall von PowerShell läuft dies darauf hinaus, externe Daten in PowerShell-Objekte zu konvertieren und umgekehrt. Dies ist der zweite Teil einer Reihe von Artikeln, die Ihnen zeigen, wie Sie fast alle gängigen Datenformate importieren und in einige von ihnen exportieren können, auf die Sie wahrscheinlich stoßen werden. Der erste Artikel der Reihe, PowerShell Data Basics: Dateibasierte Daten“ behandelt eine Vielzahl von Textformaten, von Dateien mit fester Breite, variabler Breite und rechtsbündiger Ausrichtung bis hin zu CSV, Eigenschaftslisten, INI-Dateien und JSON-Daten, und schließt mit einer Behandlung des Imports und Exports in Excel ab. Hier konzentrieren wir uns darauf, das Beste aus XML herauszuholen.
Zugriff auf XML-Daten in PowerShell
Es gibt zwei integrierte Techniken für die Arbeit mit XML-Daten in PowerShell: den XPath-Ansatz und den Objektpunkt-Notationsansatz. Wir werden diese beiden Ansätze beschreiben und vergleichen und sie an einigen XML-Beispielen ausprobieren. Der Einfachheit halber wird in allen Codebeispielen diese XML-Beispieldatei von MSDN verwendet. Abbildung 1 zeigt eine Darstellung des Schemas, das der Datei zugrunde liegt, in einem übersichtlichen Format (insbesondere im Vergleich zum Lesen der rohen XSD-Datei!), mit freundlicher Genehmigung des XML-Schema-Explorers von Visual Studio. In der Abbildung sehen Sie, dass die XML-Datei ein Katalog ist, der eine Sammlung von Büchern enthält. Jedes Buch hat sieben Merkmale, von denen sechs untergeordnete Elemente und eines ein untergeordnetes Attribut ist.
Abbildung 1 Schema für Microsofts XML-Beispieldateio:p>
Um diese XML-Beispieldatei zu laden, können Sie eines der folgenden Verfahren verwenden:
Wenn Sie lieber mit sofortigen XML-Daten experimentieren möchten, anstatt eine XML-Datei in ein XmlDocument zu laden, ist das ganz einfach. Definieren Sie einfach Ihr XML als String und wandeln Sie es in den XML-Typ um, wie wir es gerade mit dem Cmdlet „Get-Content“ für Dateien getan haben. Hier ist ein Teil der XML-Beispieldatei mit nur zwei Büchern:
Zugriff auf XML mit XPath
Nachdem die Datei in ein XmlDocument-Objekt geladen wurde, können Sie mit XPath in der XML-Struktur navigieren. Um eine Gruppe von Knoten auszuwählen, verwenden Sie die Methode SelectNodes:
$xdoc.SelectNodes(„//autor“)
#text
—
Gambardella, Matthew
Ralls, Kim
Corets, Eva
Corets, Eva
Corets, Eva
Randall, Cynthia
Thurman, Paula
Knorr, Stefan
Kress, Peter
O’Brien, Tim
O’Brien, Tim
Galos, Mike
Oder verwenden Sie SelectSingleNode, um nur einen Knoten zurückzugeben:
$xdoc.SelectSingleNode(„//buch“)
id : bk102
author : Ralls, Kim
Titel : Midnight Rain
Genre : Fantasy
Preis : 5.95
Veröffentlichungsdatum : 2000-12-16
Beschreibung : Eine ehemalige Architektin kämpft gegen Firmenzombies,
eine böse Zauberin und ihre eigene Kindheit, um Königin
der Welt zu werden.
Beachten Sie im ersten Beispiel, dass es Duplikate gibt. Nehmen Sie stattdessen an, dass Sie eine Liste der eindeutigen Autoren im Katalog haben möchten. Sie könnten denken, dass etwas wie …
1
|
$xdoc.SelectNodes(„//author“) | select -Unique
|
…würde funktionieren, aber es gibt nur den Namen des ersten Autors zurück. Um zu verstehen, warum das nicht funktioniert, müssen Sie mehr über die Struktur von XML-Dokumenten wissen. Das erste Beispiel gibt eine Liste von Textknoten zurück (also keine Liste von Text), und der eindeutige Filter sucht in dieser Liste nach der Einzigartigkeit des Elementtyps. Da alle Elemente in der Sammlung Textknoten sind und somit alle denselben Elementtyp haben, werden alle Knoten nach dem ersten als Duplikate betrachtet. Das Ergebnis ist, dass nur der erste zurückgegeben wird.
Was Sie wirklich suchen, ist der String-Wert jedes Autorenknotens. Vom Autorenknoten aus müssen Sie zuerst auf seinen Textknoten (sein erstes und einziges Kind) zugreifen und dann auf den Wert dieses Textknotens (Zeile A im nächsten Beispiel). Alternativ können Sie auch einen etwas kürzeren Ausdruck mit der Eigenschaft InnerText verwenden (Zeile B). In einer weiteren Variante wird das Cmdlet „Select-Xml“ verwendet, das einen Methodenaufruf vermeidet und somit in gewisser Weise ein eindeutigerer PowerShell-Ansatz ist (Zeile C). Alle drei Zeilen geben das gleiche Ergebnis zurück.
SelectNodes und SelectSingleNode bieten zusammen die gleiche Funktionalität wie Select-Xml. Beide unterstützen Namespaces, die ich noch nicht erwähnt habe. Die beiden Methoden nehmen beide einen XmlNamespaceManager als optionalen zweiten Parameter an, während das Cmdlet Select-Xml einen optionalen Namespace-Parameter annimmt, der Ihre Namespaces in einer Hashtabelle angibt.
SelectSingleNode gibt einen XmlNode zurück und SelectNodes gibt eine XmlNodeList zurück. Select-Xml hingegen gibt ein SelectXmlInfo-Objekt (oder ein Array davon) zurück und seine Node-Eigenschaft ermöglicht den Zugriff auf den zugrunde liegenden Knoten. Das obige Beispiel veranschaulicht diese Unterschiede.
Zugriff auf XML als Objekte
Mit demselben XmlDocument-Objekt aus dem letzten Abschnitt bietet PowerShell auch dynamische Objektunterstützung für XML-Daten: Damit können Sie auf XML-Daten als PowerShell-Objekte erster Klasse zugreifen, wofür weder ein XPath-Selektor noch Kenntnisse über die Details von XML-Knoten, Werten oder Textknoten erforderlich sind. Außerdem erhalten Sie sofortige, dynamische Intellisense-Funktionalität für Ihr XML-Schema, wenn Sie Ihre XML-Daten laden! Abbildung 2 veranschaulicht dies für die PowerShell ISE, wo Sie sowohl Auswahlmöglichkeiten als auch Wortvervollständigung erhalten, genau wie bei den nativen PowerShell-Tokens. Beachten Sie insbesondere in der unteren Erweiterung, dass nach dem gesucht wird, was Sie an einer beliebigen Stelle im Eigenschaftsnamen eingegeben haben, und nicht nur ab dem ersten Zeichen: Intellisense würde hier eine Datumseigenschaft finden, egal ob sie published_date oder releaseDate oder date_of_publication heißt. Beachten Sie, dass die Wortvervollständigung in PowerShell V2 oder V3 sowie in PowerShell ISE oder PowerShell-Konsole verfügbar ist. Die Auswahlmöglichkeiten sind jedoch nur in PowerShell ISE in V3 verfügbar.
Abbildung 2 Automatische Intellisense beim Laden eines XML-Dokuments
Hier sind einige Beispiele, die zeigen, dass das XML tatsächlich automatisch in PowerShell-Objekte konvertiert wird:
$xdoc
xml catalog
– —
version=“1.0″ catalog
$xdoc.catalog
book
—
{book, book, book, book, book…}
$xdoc.catalog.book | Format-Table -AutoSize
id author title genre price publish_date description
– — — — — —- —-
bk101 Gambardella, Matthew XML Developer’s Guide Computer 44.95 2000-10-01 Ein eingehender Blick …
bk102 Ralls, Kim Mitternachtsregen Fantasy 5.95 2000-12-16 Ein ehemaliger Architekt …
bk103 Corets, Eva Maeve Aszendent Fantasy 5.95 2000-11-17 Nach dem Zusammenbruch …
bk104 Corets, Eva Oberons Erbe Fantasy 5.95 2001-03-10 In der Postapokalyps…
bk105 Corets, Eva Der versunkene Gral Fantasy 5.95 2001-09-10 Die beiden Töchter…
bk106 Randall, Cynthia Lover Birds Romance 4.95 2000-09-02 Als Carla auf …
bk107 Thurman, Paula Splish Splash Romance 4.95 2000-11-02 Ein Tiefseetaucher …
bk108 Knorr, Stefan Creepy Crawlies Horror 4.95 2000-12-06 Eine Anthologie von h…
bk109 Kress, Peter Paradox Lost Science Fiction 6.95 2000-11-02 Nach einer versehentlichen…
bk110 O’Brien, Tim Microsoft .NET: The Prog… Computer 36.95 2000-12-09 Microsofts .NET …
bk111 O’Brien, Tim MSXML3: A Comprehensive … Computer 36.95 2000-12-01 The Microsoft MSX…
bk112 Galos, Mike Visual Studio 7: A Compr… Computer 49.95 2001-04-16 Microsoft Visual …
$xdoc.catalog.book
id : bk103
author : Corets, Eva
title : Maeve Ascendant
genre : Fantasy
Preis : 5.95
publish_date : 2000-11-17
description : Nach dem Zusammenbruch einer nanotechnologischen
Gesellschaft in England, legen die jungen Überlebenden den
Grundstein für eine neue Gesellschaft
$xdoc.catalog.book.author
Randall, Cynthia
$xdoc.catalog.book.id
bk106
Beachten Sie, dass alle XML-Knoten im Dokument in standardmäßige PowerShell-Eigenschaften konvertiert werden, unabhängig davon, ob ein Knoten Kinder hat (z. B. catalog) oder ein Blattknoten ist (z. B. price) oder ob ein Blattknoten ein Element (z. B. author) oder ein Attribut (z. B. id) ist. Insbesondere (wie die letzten beiden Beispiele oben zeigen) werden Element- und Attributwerte mit der Standard-„Punkt“-Notation genau gleich behandelt.
Vergleich von XPath- und Objektansätzen
Welcher Ansatz ist besser für den Zugriff auf XML-Daten? Tabelle 1 hilft Ihnen bei der Beantwortung dieser Frage. Der Objektansatz ist in der Regel prägnanter (z.B. Zeile 3), aber nicht immer (Zeile 4). XPath ist jedoch ausdrucksstärker, da es die Angabe einiger Selektoren ermöglicht, die mit der Objektnotation nicht möglich sind (Zeile 7). Die PowerShell-eigenen Funktionen können diese Lücke jedoch leicht schließen, wenn die Objektnotation verwendet wird (Zeile 8).
Tabelle 1 Vergleich von XPath- und Objektselektoransätzen für den XML-Zugriff.
Ändern oder Erstellen von XML-Daten
Mit den Kenntnissen über XML-Selektoren aus den vorherigen Abschnitten ist das Ändern eines XML-Dokuments recht einfach, da XML-Selektoren (entweder XPath oder Objekt) L-Werte sind, d. h. Sie können sowohl in sie schreiben als auch aus ihnen lesen! So wird einer der beiden den Autor des 6. Buches ändern:
1
2
|
$xdoc.SelectSingleNode(„//book/author“).InnerText = ‚jones‘
$xdoc.catalog.book.author = ’smith‘
|
Es kommt häufig vor, dass Sie eine bestehende XML-Datei haben, in der Sie einen oder mehrere Knotenwerte ändern möchten. Wahrscheinlich möchten Sie die Datei lesen, die Daten ändern und die Datei wieder unter demselben Namen speichern. Sie haben die ersten beiden Schritte gesehen; der dritte Schritt wird mit der Save-Methode auf dem XmlDocument durchgeführt. Wenn man all diese Schritte zusammenfügt, ergibt sich folgender einfacher Code:
Dieser Code läuft einwandfrei – außer dass er in den meisten Fällen fehlgeschlagen zu sein scheint! Dieses einfache Stück Code veranschaulicht einen scheinbar unbedeutenden, aber wichtigen PowerShell-Befehl; die Unkenntnis darüber hat zu vielen Blogbeiträgen geführt, in denen behauptet wird, dass es sich um einen Fehler in PowerShell handelt. Das Problem ist, dass Ihr Arbeitsverzeichnis und Ihr PowerShell-Speicherort nicht dasselbe sind. Der obige Code liest die Datei und ändert die Daten, aber er speichert die neue Datei nicht unbedingt dort, wo Sie sie erwarten. Get-Content, ein PowerShell-Cmdlet, sieht einen Dateipfad relativ zum PowerShell-Speicherort. Die Methode XmlDocument.Save hingegen sieht einen Dateipfad relativ zum Arbeitsverzeichnis des PowerShell-Prozesses, da dieser Methodenaufruf außerhalb von PowerShell erfolgt. Wenn Sie Set-Location (oder dessen Alias cd) in Ihrer aktuellen PowerShell-Sitzung nicht ausgeführt haben, verweisen beide auf dasselbe Verzeichnis. Um dies zu bestätigen, führen Sie diese beiden Anweisungen aus:
1
2
|
Get-Location # zeigt PowerShell-Speicherort an
::CurrentDirectory # zeigt das Arbeitsverzeichnis an
|
Am sichersten ist es also, absolute Pfade zu verwenden, um dieses Problem ganz zu vermeiden. Weitere Informationen finden Sie in Alex Angelopoulos‘ Artikel Why the PowerShell Working Directory and the PowerShell Location Aren’t One in the Same.
Adding XML Data
Das Hinzufügen neuer Knoten zu Ihrem XML-Dokument erfordert etwas mehr Arbeit als das Ändern des Wertes eines vorhandenen Knotens. Ein Ansatz, der mir gefällt, stammt aus dem Blogeintrag Write, Add and Change XML Data von Tobias Weltner: Nehmen Sie einen bestehenden Knoten des Typs, den Sie erstellen möchten, erstellen Sie eine Kopie dieses Knotens und ändern Sie die Kopie mit Ihren neuen Daten, und fügen Sie schließlich den kopierten Knoten als Geschwister des Originals in Ihr XML ein. Übertragen auf unser Beispiel eines Buchkatalogs bedeutet dies, dass ein neuer Buchknoten erstellt und am Ende der Sammlung eingefügt wird:
Wenn Sie das neue Buch an einer anderen Stelle einfügen möchten, z. B. nach dem dritten Buch, verwenden Sie InsertAfter statt AppendChild:
1
|
$xdoc.catalog.InsertAfter($book,$xdoc.catalog.book)
|
(Der Name der Methode InsertAfter täuscht darüber hinweg, dass sie nicht nur Knoten hinzufügen, sondern auch verschieben kann! Die Methode prüft, ob der hinzuzufügende Knoten bereits im Dokument vorhanden ist. Ist dies der Fall, wird er an die von Ihnen angegebene neue Position verschoben. Wenn Sie also Knoten kopieren möchten, müssen Sie mit der Clone-Methode beginnen (siehe oben).)
Weitere Informationen zur Bearbeitung von XML-Daten mit .NET-Methoden finden Sie unter Verarbeiten von XML-Daten mithilfe des DOM-Modells auf MSDN.
Verwenden von XML für die Objektserialisierung
PowerShell bietet eine einfache Möglichkeit, Objekte aufrechtzuerhalten, indem Sie Export-Clixml verwenden, um jedes Objekt zu serialisieren und in einer XML-Datei zu speichern, und Import-Clixml, um das Objekt aus XML wiederherzustellen. Im Gegensatz zu den meisten anderen Serialisierungstechniken bleibt bei XML die Objektintegrität erhalten: Bei der Wiederherstellung eines Objekts aus XML sind alle Eigenschaften korrekt typisiert, ebenso wie das übergeordnete Objekt selbst, so dass alle Methoden des ursprünglichen Objekts auch für das neu generierte Objekt verfügbar sind. Um Export-Clixml zu verwenden, leiten Sie einfach eine beliebige Objektsammlung an das Programm weiter und geben eine Zieldatei an. Hier ist ein einfaches Beispiel, das zeigt, dass die Ausgabe von Get-ChildItem, einer Sammlung von FileSystemInfo-Objekten, neu generiert wird: