PowerShell zum Parsen von XML: Lesen und Validieren

XML ist überall. Trotz der lästigen Verwendung von spitzen Klammern wird das XML-Format immer noch weit verbreitet eingesetzt. Konfigurationsdateien, RSS-Feeds, Office-Dateien (das „x“ in .docx) sind nur eine unvollständige Liste. Die Verwendung von PowerShell zum Parsen von XML-Dateien ist ein wesentlicher Schritt auf Ihrer PowerShell-Reise.

In diesem Tutorial wird Ihnen gezeigt, wie PowerShell XML-Dateien analysiert und validiert. Dabei werden Sie in allen Aspekten des Abrufens und Auswertens von XML-Daten von Null auf Held geführt. Ihnen werden Tools zur Verfügung gestellt, mit denen Sie die Integrität von XML-Daten überprüfen und fehlerhafte Daten sofort an der Eingangsschleuse Ihrer Skripte stoppen können!

Voraussetzungen

Um den präsentierten Inhalt nachvollziehen zu können, sollten Sie Folgendes haben:

  • PowerShell Version 3.0 oder höher. Die Beispiele wurden mit Windows PowerShell v5.1 erstellt
  • Notepad++, Visual Studio Code oder einen anderen Texteditor, der XML versteht.

Parsen von PowerShell XML-Elementen mit Select-Xml

Lassen Sie uns zuerst eine der beliebtesten und einfachsten Möglichkeiten zur Verwendung von PowerShell zum Parsen von XML behandeln: Select-Xml. Das Select-Xml-Cmdlet ermöglicht es Ihnen, eine XML-Datei oder Zeichenkette zusammen mit einem „Filter“ namens XPath bereitzustellen, um bestimmte Informationen herauszuziehen.

XPath ist eine Kette von Elementnamen. Es verwendet eine „pfadähnliche“ Syntax, um Knoten in einem XML-Dokument zu identifizieren und zu navigieren.

Angenommen, Sie haben eine XML-Datei mit einer Reihe von Computern und möchten PowerShell verwenden, um diese XML-Datei zu analysieren. Jeder Computer hat verschiedene Elemente wie Name, IP-Adresse und ein Include-Element zur Aufnahme in einen Bericht.

Ein Element ist ein XML-Abschnitt mit einem öffnenden Tag und einem schließenden Tag, möglicherweise mit etwas Text dazwischen, wie zum Beispiel <Name>SRV-01</Name>

<Computers>
	<Computer>
		<Name>SRV-01</Name>
		<Ip>127.0.0.1</Ip>
		<Include>true</Include>
	</Computer>	
	<Computer>
		<Name>SRV-02</Name>
		<Ip>192.168.0.102</Ip>
		<Include>false</Include>
	</Computer>	
	<Computer>
		<Name>SRV-03</Name>
		<Ip>192.168.0.103</Ip>
		<Include>true</Include>
	</Computer>	
</Computers>

Sie möchten PowerShell verwenden, um diese XML-Datei zu analysieren und die Computernamen zu erhalten. Dazu könnten Sie den Befehl Select-Xml verwenden.

In der obigen Datei erscheinen die Computernamen im Inneren Text (InnerXML) des Elements Name.

InnerXML ist der Text zwischen den beiden Tags des Elements.

Um die Computernamen zu finden, würden Sie zunächst das entsprechende XPath (/Computers/Computer/Name) angeben. Diese XPath-Syntax würde nur die Name-Knoten unter den Computer-Elementen zurückgeben. Um nur das InnerXML jedes Name-Elements zu erhalten, greifen Sie mit einer ForEach-Object-Schleife auf die Node.InnerXML-Eigenschaft jedes Elements zu.

Select-Xml -Path C:\Work\computers-elm.xml -XPath '/Computers/Computer/Name' | ForEach-Object { $_.Node.InnerXML }

Verwenden von PowerShell zum Analysieren von XML-Attributen mit Select-Xml

Lassen Sie uns dieses Problem des Findens von Computernamen nun aus einem anderen Blickwinkel betrachten. Dieses Mal werden die Computerbeschreibungen nicht durch XML Elemente dargestellt, sondern durch XML Attribute.

Ein Attribut ist ein Schlüssel/Wert-Teil wie name="SRV-01". Attribute erscheinen immer innerhalb des öffnenden Tags, direkt nach dem Tag-Namen.

Unten ist die XML-Datei mit den durch Attribute dargestellten Computerbeschreibungen. Sie können nun jeden Beschreiber als Attribut anstatt als Element sehen.

<Computers>
	<Computer name="SRV-01" ip="127.0.0.1" include="true" />
	<Computer name="SRV-02" ip="192.168.0.102" include="false" />
	<Computer name="SRV-03" ip="192.168.0.103" include="true" />
</Computers>

Da jeder Beschreiber dieses Mal ein Attribut ist, passen Sie den XPath ein wenig an, um nur die Computer-Elemente zu finden. Dann finden Sie mit dem ForEach-Object Cmdlet erneut den Wert des Name Attributs.

Select-Xml -Path C:\Work\computers-attr.xml -XPath '/Computers/Computer' | ForEach-Object { $_.Node.name }

Und tatsächlich liefert dies auch die gleichen Ergebnisse: SRV-01, SRV-02 und SRV-03 :

Reading data using Select-Xml

In beiden Fällen, ob Sie nun Elemente oder Attribute lesen, ist die Syntax von Select-Xml umständlich: Sie zwingt Sie dazu, den XPath Parameter zu verwenden, das Ergebnis dann an eine Schleife weiterzuleiten und schließlich unter der Knoten Eigenschaft nach den Daten zu suchen.

Zum Glück bietet PowerShell eine bequemere und intuitivere Möglichkeit, XML-Dateien zu lesen. PowerShell ermöglicht es Ihnen, XML-Dateien zu lesen und sie in XML Objekte umzuwandeln.

Verwandt: Verwendung von PowerShell-Datentyp-Beschleunigern zur Beschleunigung der Codierung

Umwandlung von XML-Strings in Objekte

Eine weitere Möglichkeit, PowerShell zum Parsen von XML zu verwenden, besteht darin, dieses XML in Objekte zu konvertieren. Der einfachste Weg, dies zu tun, ist mit dem [xml] Typbeschleuniger.

Indem Sie die Variablennamen mit [xml] versehen, konvertiert PowerShell das ursprüngliche einfache Text-XML in Objekte, mit denen Sie dann arbeiten können.

[xml]$xmlElm = Get-Content -Path C:\Work\computers-elm.xml
[xml]$xmlAttr = Get-Content -Path C:\Work\computers-attr.xml

Lesen von XML-Objektelementen

Jetzt sind sowohl die Variablen $xmlElm als auch $xmlAttr XML-Objekte, auf die Sie über die Punktnotation zugreifen können. Möglicherweise müssen Sie die IP-Adresse jedes Computerelements finden. Da die XML-Datei ein Objekt ist, können Sie dies einfach tun, indem Sie auf das IP-Element verweisen.

$xmlElm.Computers.Computer.ip

Ab PowerShell-Version 3.0 wird der XML-Objekt den Attributwert mit der gleichen Syntax abgerufen, die zum Lesen des Textes innerhalb des Elements verwendet wird. Daher werden die IP-Adresswerte aus der Attributdatei mit der genau gleichen Syntax wie in der Elementdatei gelesen.

Lesen von XML-Attributen

Mit genau derselben Punktnotation können Sie auch XML-Attribute lesen, obwohl es Unterschiede in der XML-Struktur gibt.

$xmlElm.Computers.Computer.ip
$xmlAttr.Computers.Computer.ip

Und die folgenden Ergebnisse zeigen, dass beide die gleichen Daten erhalten haben, jeder aus seiner entsprechenden Datei:

PowerShell to Parse XML : Reading XML Attributes

Darüber hinaus erhalten Sie mit dem Objekt, sobald es im Speicher geladen ist, sogar IntelliSense zur automatischen Vervollständigung, wenn Sie die PowerShell ISE verwenden. Wie unten gezeigt.

IntelliSense and tab completion for XML object

Durchlaufen von XML-Daten

Sobald Sie XML-Dateien direkt in XML-Objekte einlesen (indem Sie den [xml] Typbeschleuniger verwenden), haben Sie die volle Leistungsfähigkeit von PowerShell-Objekten zur Verfügung.

Nehmen wir zum Beispiel an, Sie müssen durch alle Computer in der XML-Datei iterieren, die das Attribut include=“true“ haben, um ihren Verbindungsstatus zu überprüfen. Der folgende Code zeigt, wie dies gemacht werden kann.

Das Skript ist:

  • Lesen der Datei und Umwandlung in ein XML-Objekt.
  • Iteration durch die relevanten Computer, um ihren Verbindungsstatus zu erhalten.
  • Schließlich das Ausgaberesultatobjekt an die Pipeline senden.
 ## Umwandlung des Dateitextes in ein XML-Objekt
 [xml]$xmlAttr = Get-Content -Path C:\Work\computers-attr.xml

 ## Schleife durch Computer mit include="true" gesetzt
 $xmlAttr.Computers.Computer | Where-Object include -eq 'true' |  ForEach-Object {
     ## Überprüfen, ob der aktuelle Computer online ist
     if(Test-Connection -ComputerName $_.ip -Count 1 -Quiet)
     {
         $status = 'Connection OK'
     }
     else
     {
         $status = 'No Connection'
     }

     ## Ausgabe des Ergebnisobjekts
     [pscustomobject]@{
         Name = $_.name
         Ip = $_.ip
         Status = $status
     }
 }

Und die Ergebnisse des obigen Skripts werden unten angezeigt:

Connection status results

XML-Schemas

In dem vorherigen Abschnitt haben Sie zwei verschiedene XML-Dateien gesehen, die eine Datensatz auf zwei verschiedene Arten darstellen. Diese Arten werden XML-Schemas genannt. Ein XML-Schema definiert die zulässigen Bausteine eines spezifischen XML-Dokuments:

  • Die Namen von Elementen und Attributen, die in diesem spezifischen Dokument erscheinen können.
  • Die Anzahl und Reihenfolge der Unterelemente.
  • Die Datentypen für Elemente und Attribute.

Das Schema definiert im Wesentlichen die Struktur des XML.

Validierung von XML-Daten

Eine XML-Datei kann die richtige Syntax haben (Editoren wie Notepad++ würden sich beschweren, wenn nicht), aber ihre Daten könnten nicht den Projektanforderungen entsprechen. Hier kommt das Schema ins Spiel. Wenn Sie sich auf XML-Daten verlassen, müssen Sie sicherstellen, dass alle Daten gemäß dem definierten Schema gültig sind.

Das Letzte, was Sie wollen, ist, Datenfehler zur Laufzeit zu entdecken, 500 Zeilen tief im Skript. Zu diesem Zeitpunkt könnte es bereits einige irreversible Operationen auf dem Dateisystem und der Registrierung durchgeführt haben.

Also, wie können Sie im Voraus überprüfen, ob die Daten korrekt sind? Lassen Sie uns zunächst einige mögliche Fehlerarten betrachten.

Mögliche Fehler in den XML-Daten

Im Allgemeinen gehören Fehler, die in XML-Dateien gefunden werden, zu einer von zwei Kategorien: Metadatenfehler und Fehler in den Daten selbst.

XML-Metadatenfehler

Diese Datei MorePeople.xml unten ist syntaktisch einwandfrei gültig. Sie können unten sehen, dass die Datei ein einzelnes People Element (das Stammelement) mit drei Person Elementen enthält. Diese Struktur ist vollkommen akzeptabel. Es enthält jedoch eine Ausnahme, können Sie sie sehen?

<People>
	<Person Name="Debra" County="Canada" IsAdmin="true" />
	<Person Name="Jacob" Country="Israel" IsAdmin="true" />
	<Person Name="Olivia" Country="Cyprus" IsAdmin="false" />
</People>

Machen Sie sich keine Sorgen, wenn Sie es nicht sehen, es versteckt sich nur. Das Problem liegt im ersten inneren Element:

Was hätte ein Country sein sollen, wurde falsch geschrieben und Kanada wurde zu einem County degradiert.

Fehler in den XML-Daten

Nachdem das Country Problem in MorePeople.xml behoben wurde, ist ein weiteres Problem aufgetreten:

<People>
	<Person Name="Debra" Country="Canada" IsAdmin="yes" />
	<Person Name="Jacob" Country="Israel" IsAdmin="true" />
	<Person Name="Olivia" Country="Cyprus" IsAdmin="false" />
</People>

Die Metadaten, d.h. die Elemente und Attribute, sind in Ordnung. Also, was ist das Problem? Dieses Mal liegt das Problem, wieder in der ersten Person -Zeile, an einem der Werte. Jemand hat entschieden, dass ja ein ausreichender Ersatz für wahr ist – aber der folgende Code wird das erste Element nicht finden, da er nach wahr sucht, nicht nach ja:

$peopleXml.People.Person | Where-Object IsAdmin -eq 'true'

Erstellen eines XML-Schemas

Jetzt, da Sie wissen, welche Arten von Fehlern auftreten können, ist es an der Zeit zu zeigen, wie eine Schemadatei hilft. Der erste Schritt besteht darin, eine Beispieldatei zu erstellen. Das Beispiel kann das kleinste Beispiel sein und nichts anderes als ein einziges inneres Element enthalten. Für die obigen Beispiele erstellen wir eine Beispieldatei wie folgt: People.xml:

<People>
	<Person Name="Jeff" Country="USA" IsAdmin="true" />
</People>

Erstellen Sie nun eine PowerShell-Funktion unten und verwenden Sie sie mit den Beispieldaten, um das .xsd-Schema zu erstellen.

function New-XmlSchema
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory)]
        [ValidateScript({ Test-Path -Path $_ })]
        [ValidatePattern('\.xml')]
        [string]$XmlExample
    ) 

    $item = Get-Item $XmlExample
    $dir = $item.Directory
    $name = $item.Name
    $baseName = $item.BaseName

    ## Erstellen Sie den Schemapfad, indem Sie '.xml' durch '.xsd' ersetzen
    $SchemaPath = "$dir\$baseName.xsd"

    try
    {
        $dataSet = New-Object -TypeName System.Data.DataSet
        $dataSet.ReadXml($XmlExample) | Out-Null
        $dataSet.WriteXmlSchema($SchemaPath)
        
        ## Zeige die erstellte Schemadatei
        Get-Item $SchemaPath
    }
    catch
    {
        $err = $_.Exception.Message
        Write-Host "Failed to create XML schema for $XmlExample`nDetails: $err" -ForegroundColor Red
    }
}

Kopieren Sie die Funktion in Ihre ISE oder Ihren bevorzugten PowerShell-Editor, laden Sie sie in den Arbeitsspeicher und verwenden Sie sie zum Erstellen des Schemas. Mit der geladenen Funktion lautet der Code zum Erstellen des Schemas wie folgt:

New-XmlSchema -XmlExample 'C:\Work\People.xml'

Die Ergebnisse zeigen den Pfad zum neu erstellten Schema:

Creating XML schema from a sample data file

Verwenden der Schemadatei zur Überprüfung Ihrer Daten

Werfen Sie einen Blick auf den oben angegebenen Speicherort. Wenn die .xsd-Datei vorhanden ist, sind Sie auf dem richtigen Weg, um die Validierung in Aktion zu sehen. Verwenden Sie für den Bestätigungsschritt die folgende Funktion:

function Test-XmlBySchema
{
    [CmdletBinding()]
    [OutputType([bool])]
    param
    (
        [Parameter(Mandatory)]
        [ValidateScript({ Test-Path -Path $_ })]
        [ValidatePattern('\.xml')]
        [string]$XmlFile,
        [Parameter(Mandatory)]
        [ValidateScript({ Test-Path -Path $_ })]
        [ValidatePattern('\.xsd')]
        [string]$SchemaPath
    )

    try
    {
        [xml]$xml = Get-Content $XmlFile
        $xml.Schemas.Add('', $SchemaPath) | Out-Null
        $xml.Validate($null)
        Write-Verbose "Successfully validated $XmlFile against schema ($SchemaPath)"
        $result = $true
    }
    catch
    {
        $err = $_.Exception.Message
        Write-Verbose "Failed to validate $XmlFile against schema ($SchemaPath)`nDetails: $err"
        $result = $false
    }
    finally
    {
        $result
    }
}

Laden Sie die Funktion in den Arbeitsspeicher und verwenden Sie sie, um die Datei MorePeople.xml anhand der beiden Fehlerbeispiele zu validieren. Verwenden Sie den folgenden Befehl, um die Validierung auszulösen:

Test-XmlBySchema -XmlFile 'C:\Work\MorePeople.xml' -SchemaPath 'C:\Work\People.xsd' -Verbose

Die tatsächlichen Ergebnisse hängen vom Inhalt der Datei MorePeople.xml ab.

Schauen wir uns zwei Beispiele an. Beachten Sie, dass die Funktion oben True zurückgibt, wenn MorePeople.xml fehlerfrei ist.

Validation success

Wenn die Datei MorePeople.xml falsche Daten enthält (das Schlüsselwort Country falsch geschrieben als County), gibt die Funktion einige Fehlerdetails zurück und gibt False zurück.

Validation failure – wrong attribute name detected

Wie Sie sehen können, ist der in der ausführlichen Ausgabe angegebene Fehler sehr informativ: Er verweist auf die fehlerhafte Datei und zeigt auf das genaue Element darin, an dem das Problem aufgetreten ist.

Feinabstimmung des Validierungsschemas

Schauen wir uns die Schemadatei an und sehen wir, wie wir sie noch besser machen können.

Das Schema, das standardmäßig von New-XmlSchema erstellt wird, sieht wie folgt aus:

<?xml version="1.0" standalone="yes"?>
<xs:schema id="People" xmlns="" xmlns:xs="<http://www.w3.org/2001/XMLSchema>" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="People" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
    <xs:complexType>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="Person">
          <xs:complexType>
            <xs:attribute name="Name" type="xs:string" />
            <xs:attribute name="Country" type="xs:string" />
            <xs:attribute name="IsAdmin" type="xs:string" />
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:schema>

Das oben gezeigte Standard-Schema ist gut, aber nicht perfekt. Es hat tatsächlich den Tippfehler mit dem Attribut Country erkannt. Wenn Sie das Schema jedoch unverändert lassen, werden andere nicht erfüllte Erwartungen nicht als Fehler von der Validierungsfunktion Test-XmlBySchema gemeldet. Lassen Sie uns das lösen.

Die Tabelle unten zeigt einige Fälle, die nicht als Validierungsfehler angesehen werden und von Test-XmlBySchema unbemerkt bleiben. In jeder Zeile zeigt die rechte Spalte, wie das Schema manuell geändert werden kann, um den erforderlichen Schutz hinzuzufügen.

A modified version of the schema file, with all protections added, is shown right after the table.

Hinzufügen einer Einstellung zum Standardschema – Beispiele

Required Behavior Relevant setting on the schema file
At least one Person element Set the minOccurs value of the Person element to 1
Make Name, Country and IsAdmin attributes mandatory Add use=”required” to each of these attributes declaration
Allow only true/false values for IsAdmin Set type=”xs:boolean” for the IsAdmin declaration
Allow only Country names between 3 and 40 characters Use the xs:restriction (see detailed explanation after the schema text)
<?xml version="1.0" standalone="yes"?>
<xs:schema id="People" xmlns="" xmlns:xs="<http://www.w3.org/2001/XMLSchema>" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="People" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
    <xs:complexType>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="Person">
          <xs:complexType>
            <xs:attribute name="Name" type="xs:string" use="required" />
	    <xs:attribute name="Country" use="required">
	      <xs:simpleType>
		<xs:restriction base="xs:string">
		  <xs:minLength value="3"/>
		  <xs:maxLength value="40"/>
		</xs:restriction>
	      </xs:simpleType>
	    </xs:attribute>	
            <xs:attribute name="IsAdmin" type="xs:boolean" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:schema>

Mit der boolschen Beschränkung für das Attribut IsAdmin im Beispiel muss sein Wert klein geschrieben true oder false

Zeichenlängenvalidierung mit xs:restriction

Die Zeichenlängenvalidierung ist etwas komplex. Obwohl sie oben als Teil des modifizierten Schemas gezeigt wird, verdient sie etwas mehr Aufmerksamkeit.

Das ursprüngliche Schemaelement für das Attribut Country (nachdem use=“required“ manuell hinzugefügt wurde) lautet wie folgt:

<xs:attribute name="Country" type="xs:string" use="required" />

Um den Längenschutz hinzuzufügen, sollten Sie das Element <xs:simpleType> hinzufügen und darin das <xs:restriction base="xs:string">. Diese Beschränkung enthält wiederum die erforderlichen Grenzwerte, die in xs:minLength und xs:minLength angegeben sind.

Nach all diesen Änderungen ist die endgültige xs:attribute-Deklaration von einer einzelnen Zeile zu einem riesigen 8-zeiligen Knoten geworden:

<xs:attribute name="Country">
  <xs:simpleType>
	<xs:restriction base="xs:string">
	  <xs:minLength value="3"/>
	  <xs:maxLength value="40"/>
	</xs:restriction>
  </xs:simpleType>
</xs:attribute>

Wenn Ihr Kopf nach der obigen Erklärung nicht wirbelt, haben Sie das Recht, die Validierung in Aktion zu sehen. Um dies zu tun, verkürzen wir absichtlich den Wert Canada auf eine zweisilbige Zeichenkette: Ca

Mit dem verkürzten Ländernamen und der gespeicherten Datei MorePeople.xml sind Sie bereit, den folgenden Validierungsbefehl auszuführen:

Test-XmlBySchema -XmlFile 'C:\Work\MorePeople.xml' -SchemaPath 'C:\Work\People.xsd' -Verbose

und die Ergebnisse zeigen tatsächlich, dass das komplexe Schema seine Arbeit geleistet hat:

A string length error detected by the schema validation

XML-Schema-Validierung kann an Komplexität zunehmen und nahezu jedes denkbare Muster validieren, insbesondere in Kombination mit regulären Ausdrücken.

Source:
https://adamtheautomator.com/powershell-parse-xml/