XML in PowerShell: Elemente und Attribute auslesen, Textknoten anzeigen


    Tags: ,

    XML-DokumentXML hat sich als For­mat für struk­turierte Infor­mationen etabliert, beispiels­weise für Kon­figu­rations­dateien oder den Daten­austausch. Power­Shell kann XML-Doku­mente als Objekte an­sprechen und XML-Elemente als deren Eigen­schaften. Hinzu kommen zahl­reiche Methoden für den Zugriff auf Elemente und Attribute.

    PowerShell verfolgt bei XML-Dokumenten grundsätzlich den gleichen Ansatz wie bei CSV-Dateien. Dort schirmt es den Benutzer von den Einzel­heiten des Formats ab, so dass er sich nicht mit Überschriften, Spalten oder Trennzeichen herumschlagen muss. PowerShell übernimmt auch die grobe Arbeit beim Parsen von XML-Strukturen.

    XML-Dokumente einlesen

    Damit PowerShell eine XML-Datei als XML-Objekt darstellt, muss man sie an eine Variable vom Typ XML übergeben. Die Deklaration des Datentyps übernimmt dabei ein Cast. Das anschließende Einlesen erfolgt wie bei anderen Textdateien mit Get-Content:

    [xml]$ovf = Get-Content -Path '.\MyVM.ovf'

    Dieses Beispiel liest den Inhalt einer .ovf-Datei, die dem Austausch von virtuellen Maschinen zwischen verschiedenen Hypervisoren dient, in die Variable $ovf ein.

    Ein alternatives Verfahren würde erst ein neues Objekt vom Typ XMLDocument erzeugen und anschließend dessen load-Methode verwenden, um die XML-Datei zu importieren:

    $ovf = New-Object System.XML.XMLDocument
    $ovf.Load(".\MyVM.xml")

    Durch den DOM-Baum navigieren

    Das XML-Objekt erlaubt nun eine unkomplizierte Navigation durch die Hierarchie mit Hilfe des Punkt-Operators. Nachfolger eines Elements lassen sich auf diese Weise einfach aneinander­reihen. PowerShell unterstützt diesen Vorgang durch die Autover­vollständigung über die Tab-Taste.

    In unserem Beispiel könnten wir ausgehend vom Wurzelelement mit

    $ovf.Envelope.VirtualSystem.VirtualHardwareSection.Info

    das Element Info unterhalb von VirtualHardwareSection ansprechen. Nachdem dieses nur einen Textknoten enthält, wird einfach nur dessen Inhalt ausgegeben. Benötigt man auch das Markup, dann würde man dieses über die Eigenschaft OuterXml erhalten:

    $ovf.Envelope.VirtualSystem.VirtualHardwareSection.Info.OuterXml

    Navigation durch die XML-Hierarchie mit Hilfe des Punkt-Operators

    Würden wir hingegen, wie im folgenden Fragment dargestellt, die Elemente Item ansteuern, dann bestünde die Ausgabe in einer Liste von Elementen bzw. Attributen und den Werten, die in jedem Item enthalten sind.

    Der dafür zuständige Ausdruck sähe dann so aus:

    $item = $ovf.Envelope.VirtualSystem.VirtualHardwareSection.Item

    Will man auf das Element Description unterhalb des ersten Item zugreifen, dann könnte man das tun, indem man einen Index verwendet:

    $ovf.Envelope.VirtualSystem.VirtualHardwareSection.Item[0].Description

    Einzelne Elemente in einer Sammlung lassen sich über einen Index ansprechen.

    Möchte man alle in einem Node enthaltenen Text­knoten ausgeben, dann gelingt das mit der Eigenschaft InnerText, den Teilbaum unterhalb eines Elements zeigt man hingegen mit InnerXml an:

    $ovf.Envelope.VirtualSystem.VirtualHardwareSection.Item.InnerText

    Umgang mit Attributen

    Über den Punkt-Operator lassen sich auch Attribute ansprechen, und zwar direkt oder über die Eigenschaft attributes. Allerdings wird man durch Eingabe von

    $item.attributes

    schnell erkennen, dass dabei nur der Name der Attribute ohne Präfix ausgegeben wird. Benötigt man ihre ganzen Namen, dann nutzt man dafür die gleichnamige Eigenschaft:

    $item.attributes.name

    Besitzen Attribute ein Namespace-Präfix, dann greift man auf die beiden Namens­bestandteile mit Prefix und LocalName zu, also mit

    $item.attributes.prefix

    $item.attributes.localname

    Für den expliziten Bezug auf den Wert eines Attributes gibt es noch die Eigen­schaft Value.

    Zugriff auf die Attribute eines XML-Dokuments

    Methoden für XML-Dokumente und Elemente

    PowerShell stellt für Objekte des Typs System.Xml.XMLDocument zahlreiche Methoden bereit, der Aufruf von

    $ovf | Get-Member -MemberType Method | measure -l

    zählt 43 davon. Für System.Xml.XmlElement sind es 32, wobei es einige Über­schneidungen mit jenen von XMLDocument gibt. Darunter finden sich mehrere, mit denen sich Knoten einfügen, klonen oder entfernen sowie Änderungen speichern lassen.

    Hier wird man aber auch fündig, wenn man Informationen aus einem XML-Dokument entnehmen will. So kann man bestimmte Attribute mit GetAttribute() abfragen, indem man ihm den Namen des Attributs als Argument übergibt:

    $item.GetAttribute("ovf:required")

    Alternativ ließe sich die Ausgabe von Attributes mit Where-Object (Alias '?') filtern:

    $item.attributes| ? Name -eq "ovf:required"

    Ein derartiger Befehl ließe sich auch nutzen, um Attribute anhand ihres Wertes zu selektieren, wenn man statt Name hier Value einsetzt:

    $item.attributes| ? Value -eq "false"| select name

    Dieses Beispiel liefert alle Attribute der Item-Elemente, die in $item gespeichert sind und deren Wert gleich false ist.

    Ob ein Element überhaupt Attribute hat, lässt sich mit HasAttribute nach dem Muster

    $item.HasAttribute("InstanceID")

    herausfinden.

    Wahlfreier Zugriff auf Elemente

    Elemente kann man nicht nur ansteuern, indem man dem Pfad vom Wurzel­element durch die gesamte Baumstruktur abwärts bis zum gewünschten Knoten folgt. PowerShell bietet in XMLDocument-Objekten mit GetElementsByTagName und GetElementById zwei Methoden, die man schon von Javascript kennt.

    Wie der Name nahelegt, übergibt man der ersten der beiden Funktionen den Namen von Elementen und erhält als Ergebnis sämtliche Vorkommnisse unabhängig von ihrer Position in der Struktur:

    $ovf.GetElementsByTagName("Info")

    Dieser Befehl würde in unserem Beispiel­dokument alle Elemente mit dem Namen Info liefern.

    Zugriff auf Elemente mittels GetElementsByTagName()

    Dagegen erhält man mit GetElementById() immer nur ein Element, weil der Wert eines id-Attributs in einem Dokument bekanntlich immer nur einmal vorkommen darf. Angenommen, unsere Datei enthielte das Fragment

    <Info id="hardware">Virtual hardware requirements</Info>

    Dann könnte man dieses Element mit

    $ovf.GetElementById("hardware")

    ansprechen.

    Täglich Know-how für IT-Pros mit unserem Newsletter

    Wir ver­wenden Ihre Mail-Adresse nur für den Ver­sand der News­letter.
    Es erfolgt keine per­sonen­be­zogene Auswertung.

    Bild von Wolfgang Sommergut
    Wolfgang Sommergut hat lang­jährige Erfahrung als Fach­autor, Berater und Kon­ferenz­sprecher zu ver­schie­denen Themen der IT. Da­ne­ben war er als System­ad­mi­ni­stra­tor und Con­sultant tätig.
    // Kontakt: E-Mail, XING, LinkedIn //

    Verwandte Beiträge

    Weitere Links

    1 Kommentar

    Die folgende Methode zum Lesen einer XML-Datei ist fehlerhaft, da sie die Kodierung der XML-Datei kaputt machen kann:
    [xml]$ovf = Get-Content -Path '.\MyVM.ovf'

    Get-Content berücksichtigt nicht das "encoding" Attribut der XML-Datei, sondern nur ein evtl. vorhandenes BOM. Wenn kein BOM vorhanden ist (oftmals bei aus dem Netz heruntergeladenen Dateien), funktioniert die Methode rein zufällig, da die meisten XML-Dateien UTF-8 kodiert sind und Get-Content standardmäßig auf UTF-8 zurückfällt, wenn kein BOM vorhanden ist.

    Die einzige wirklich korrekte Methode ist:

    $ovf = New-Object System.XML.XMLDocument
    $ovf.Load(".\MyVM.xml")

    Hier wird das Encoding-Attribut aus dem Header der XML-Datei ausgelesen und die Datei entsprechend dekodiert.