PowerShell per analizzare XML: leggere e convalidare

XML è ovunque. Nonostante il fastidioso utilizzo delle parentesi angolari, il formato XML è ancora ampiamente utilizzato. I file di configurazione, i feed RSS, i file di Office (la ‘x’ in .docx) sono solo un elenco parziale. Utilizzare PowerShell per analizzare i file XML è un passaggio essenziale nel tuo percorso PowerShell.

Questo tutorial ti mostrerà come PowerShell analizza i file XML e li convalida. Questo ti guiderà da zero ad eroe per tutti gli aspetti di acquisizione e valutazione dei dati XML. Ti verranno forniti strumenti che ti aiuteranno a convalidare l’integrità dei dati XML e a fermare i dati errati direttamente all’ingresso dei tuoi script!

Prerequisiti

Per seguire il materiale presentato, dovresti avere:

  • PowerShell versione 3.0 e successiva. Gli esempi sono stati creati su Windows PowerShell v5.1
  • Notepad++, Visual Studio Code o un altro editor di testo che comprende XML.

Analisi degli elementi XML di Powershell con Select-Xml

Iniziamo col coprire uno dei modi più popolari e più semplici per utilizzare PowerShell per analizzare XML, ovvero con Select-Xml. Il cmdlet Select-Xml ti consente di fornire un file XML o una stringa insieme a un “filtro” noto come XPath per estrarre informazioni specifiche.

XPath è una catena di nomi di elementi. Utilizza una sintassi “simile a un percorso” per identificare e navigare nei nodi di un documento XML.

Diciamo che hai un file XML con un gruppo di computer e vorresti usare PowerShell per analizzare questo file XML. Ogni computer ha vari elementi come il nome, l’indirizzo IP e un elemento Include per l’inclusione in un rapporto.

Un elemento è una porzione XML con un tag di apertura e un tag di chiusura, eventualmente con del testo in mezzo, come ad esempio <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>

Vorresti usare PowerShell per analizzare questo file XML e ottenere i nomi dei computer. Per farlo, potresti utilizzare il comando Select-Xml.

Nel file sopra, i nomi dei computer appaiono nel testo interno (InnerXML) dell’elemento Name.

InnerXML è il testo tra i due tag dell’elemento.

Per trovare i nomi dei computer, forniresti prima l’XPath appropriato (/Computers/Computer/Name). Questa sintassi XPath restituirebbe solo i nodi Name sotto gli elementi Computer. Poi, per ottenere solo l’InnerXML di ogni elemento Name, raggiungi la proprietà Node.InnerXML su ogni elemento con un ciclo ForEach-Object.

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

Utilizzo di PowerShell per analizzare gli attributi XML con Select-Xml

Ora affrontiamo questo problema di trovare i nomi dei computer da un punto di vista diverso. Questa volta, invece dei descrittori di computer rappresentati con gli elementi XML, sono rappresentati con gli attributi XML.

Un attributo è una parte chiave/valore come name="SRV-01". Gli attributi appaiono sempre all’interno del tag di apertura, subito dopo il nome del tag.

Di seguito è riportato il file XML con i descrittori di computer rappresentati con attributi. Ora è possibile vedere ogni descrittore come un attributo anziché un elemento.

<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>

Dato che ogni descrittore è un attributo questa volta, modifica leggermente l’XPath per trovare solo gli elementi del computer. Quindi, utilizzando nuovamente il cmdlet ForEach-Object, trova il valore dell’attributo name.

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

E infatti, questo porta anche gli stessi risultati: SRV-01, SRV-02 e SRV-03 :

Reading data using Select-Xml

In entrambi i casi, che si stiano leggendo elementi o attributi, la sintassi di Select-Xml è complicata: ti obbliga a utilizzare il parametro XPath, quindi a inviare il risultato a un loop e infine a cercare i dati nella proprietà Node.

Fortunatamente, PowerShell offre un modo più comodo e intuitivo per leggere i file XML. PowerShell consente di leggere file XML e convertirli in oggetti XML.

Correlato: Utilizzo degli acceleratori di tipo dati PowerShell per velocizzare la scrittura del codice

Conversione di stringhe XML in oggetti

Un altro modo per utilizzare PowerShell per analizzare XML è convertire quel XML in oggetti. Il modo più semplice per farlo è con l’acceleratore di tipo [xml].

Aggiungendo il prefisso [xml] ai nomi delle variabili, PowerShell converte l’XML di testo normale originale in oggetti con cui è possibile lavorare.

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

Lettura degli elementi dell’oggetto XML

Ora sia le variabili $xmlElm che $xmlAttr sono oggetti XML che consentono di fare riferimento alle proprietà tramite la notazione a punto. Forse è necessario trovare l’indirizzo IP di ciascun elemento computer. Poiché il file XML è un oggetto, è possibile farlo semplicemente facendo riferimento all’elemento IP.

$xmlElm.Computers.Computer.ip

A partire dalla versione 3.0 di PowerShell, l’oggetto XML ottiene il valore dell’attributo con la stessa sintassi utilizzata per la lettura del testo interno dell’elemento. Pertanto, i valori degli indirizzi IP vengono letti dal file degli attributi con la stessa sintassi del file degli elementi.

Lettura degli attributi XML

Utilizzando esattamente la stessa notazione a punto, è possibile leggere anche gli attributi XML nonostante le differenze nella struttura XML.

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

E i risultati di seguito mostrano che entrambi hanno ottenuto gli stessi dati, ciascuno dal proprio file corrispondente:

PowerShell to Parse XML : Reading XML Attributes

Inoltre, con l’oggetto, una volta caricato in memoria, si ottiene persino IntelliSense per il completamento automatico se si sta utilizzando PowerShell ISE. Ad esempio, come mostrato di seguito.

IntelliSense and tab completion for XML object

Iterazione attraverso i dati XML

Una volta che si è in grado di leggere i file XML direttamente nell’oggetto XML (sfruttando l’acceleratore di tipo [xml]), si ha a disposizione tutta la potenza degli oggetti PowerShell.

Ad esempio, supponiamo che sia necessario eseguire un ciclo su tutti i computer che compaiono nel file XML con l’attributo include = “true” per verificare lo stato della connessione. Il codice di seguito mostra come può essere fatto.

Questo script è:

  • Lettura del file e conversione in un oggetto XML.
  • Iterazione sui computer pertinenti per ottenere il loro stato di connessione.
  • Infine, invio dell’oggetto di output del risultato al pipeline.
 ## conversione del testo del file in un oggetto XML
 [xml]$xmlAttr = Get-Content -Path C:\Work\computers-attr.xml

 ## ciclo sui computer impostati con include = "true"
 $xmlAttr.Computers.Computer | Where-Object include -eq 'true' |  ForEach-Object {
     ## verifica se il computer corrente è online
     if(Test-Connection -ComputerName $_.ip -Count 1 -Quiet)
     {
         $status = 'Connection OK'
     }
     else
     {
         $status = 'No Connection'
     }

     ## output dell'oggetto risultato
     [pscustomobject]@{
         Name = $_.name
         Ip = $_.ip
         Status = $status
     }
 }

E i risultati dello script sopra sono mostrati di seguito:

Connection status results

Schemi XML

Nella sezione precedente hai visto due diversi file XML che rappresentano un set di dati in due modi diversi. Questi modi sono chiamati schemi XML. Uno schema XML definisce i blocchi di costruzione legali di un documento XML specifico:

  • I nomi degli elementi e degli attributi che possono apparire in quel documento specifico.
  • Il numero e l’ordine degli elementi figlio.
  • I tipi di dati per gli elementi e gli attributi.

Lo schema definisce essenzialmente la struttura dell’XML.

Convalida dei dati XML

Un file XML può avere la sintassi corretta (gli editor come Notepad++ segnaleranno se non lo è), ma i suoi dati potrebbero non corrispondere ai requisiti del progetto. È qui che entra in gioco lo schema. Quando si lavora con dati XML, è necessario assicurarsi che tutti i dati siano validi secondo lo schema definito.

L’ultima cosa che si desidera è scoprire errori nei dati durante l’esecuzione, 500 righe nel mezzo dello script. Potrebbe aver già eseguito alcune operazioni irreversibili sul file system e sul registro a quel punto.

Quindi, come si può verificare in anticipo che i dati siano corretti? Vediamo prima alcuni possibili tipi di errore.

Errori possibili nei dati XML

In generale, gli errori trovati nei file XML appartengono a una delle due categorie: errori nei metadati e errori nei dati stessi.

Errori nei metadati XML

Il file MorePeople.xml qui sotto ha una sintassi perfettamente valida. Puoi vedere che il file ha un unico elemento People (l’elemento radice) con tre elementi Person al suo interno. Questa struttura è perfettamente accettabile. Tuttavia, contiene un’eccezione, riesci a vederla?

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

Non preoccuparti se non l’hai notata, è appena nascosta. Il problema si trova nel primo elemento interno:

Cosa doveva essere un elemento Country è stato scritto in modo errato, e Canada è stato declassato a County.

Errori nei dati XML

Dopo aver corretto il problema del Country su MorePeople.xml, si è insinuato un altro problema.

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

I metadati, cioè gli elementi e gli attributi, sono corretti. Quindi qual è il problema? Questa volta, ancora sulla prima riga Persona , c’è un problema in uno dei valori. Qualcuno ha deciso che è un sostituto sufficiente per true – ma il codice come quello sotto non riuscirà a ottenere il primo elemento perché sta cercando true, non :

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

Creazione di uno schema XML

Ora che conosci i tipi di errori che possono verificarsi, è ora di mostrare come un file di schema aiuta. Il primo passo è creare un file di dati di esempio. L’esempio può essere il più piccolo possibile e contenere solo un elemento interno. Per gli esempi sopra, creiamo un file di esempio come questo People.xml :

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

Ora costruisci una funzione PowerShell qui sotto e usala con i dati di esempio per creare lo schema .xsd.

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

    ## costruisci il percorso dello schema sostituendo '.xml' con '.xsd'
    $SchemaPath = "$dir\$baseName.xsd"

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

Copia la funzione nel tuo ISE o nel tuo editor PowerShell preferito, caricala in memoria e usala per creare lo schema. Con la funzione caricata, il codice per creare lo schema è questa riga:

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

I risultati mostreranno il percorso dello schema appena creato:

Creating XML schema from a sample data file

Utilizzare il file di schema per convalidare i dati.

Dai un’occhiata alla posizione specificata dai risultati sopra. Se il file .xsd è lì, sei sulla strada giusta per vedere la convalida in azione. Per il passaggio di conferma, utilizza la funzione qui sotto:

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
    }
}

Carica la funzione in memoria e usala per convalidare il file MorePeople.xml dagli esempi di errore. Per avviare la convalida, utilizza il comando qui sotto:

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

I risultati effettivi dipendono dal contenuto di MorePeople.xml.

Vediamo due esempi. Nota che quando MorePeople.xml non contiene errori, la funzione sopra restituirà True.

Validation success

Quando il file MorePeople.xml contiene dati errati (la chiave Country è stata scritta erroneamente come County), la funzione restituirà alcuni dettagli di fallimento e False.

Validation failure – wrong attribute name detected

Come puoi vedere, l’errore specificato nell’output dettagliato è molto informativo: indica il file responsabile e indica l’esatto componente in esso dove si è verificato il problema.

Ottimizzazione dello schema di convalida

Diamo un’occhiata al file di schema, poi vediamo come possiamo renderlo ancora migliore.

Lo schema creato dal New-XmlSchema di default è il seguente:

<?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>

Lo schema predefinito sopra è buono, ma non perfetto. Infatti ha individuato l’errore di battitura con l’attributo Country. Tuttavia, se lasci lo schema com’è, nel caso in cui altre aspettative che potresti avere non vengano soddisfatte, questi problemi non verranno segnalati come errori dalla convalida Test-XmlBySchema. Risolviamo questo.

La tabella qui sotto presenta alcuni casi che non vengono considerati errori di convalida e passeranno inosservati da Test-XmlBySchema. Su ogni riga, la colonna di destra mostra come modificare manualmente lo schema per aggiungere il supporto necessario alla protezione.

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

Aggiunta dell’impostazione allo schema predefinito – esempi

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>

Con la restrizione booleana in atto per l’attributo IsAdmin nell’esempio, il suo valore deve essere un minuscolo true o false.

Convalida della lunghezza della stringa con xs:restriction

La convalida della lunghezza della stringa è un po’ complessa. Quindi, anche se è mostrata sopra come parte dello schema modificato, merita un po’ più di attenzione.

L’elemento dello schema originale per l’attributo Country (dopo aver aggiunto manualmente il use=”required”), è come segue:

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

Per aggiungere la protezione della lunghezza, è necessario aggiungere l’elemento <xs:simpleType>, e all’interno di esso, il <xs:restriction base="xs:string">. Questa restrizione contiene a sua volta i limiti richiesti dichiarati su xs:minLength e su xs:minLength.

A seguito di tutte queste modifiche, la dichiarazione finale dell’attributo xs:attribute è cresciuta da una singola riga a un gigantesco nodo di 8 righe:

<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>

Se la tua testa non gira dopo la spiegazione sopra, hai il diritto di vedere la convalida in azione. Per farlo, accorciamo intenzionalmente il valore Canada a una sillaba di due caratteri: Ca

Con il nome del paese abbreviato, e MorePeople.xml salvato, sei pronto per eseguire il comando di convalida qui sotto:

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

E i risultati mostrano davvero che lo schema complesso ha svolto il suo lavoro:

A string length error detected by the schema validation

La validazione dello schema XML può crescere in complessità e validare praticamente qualsiasi schema tu possa immaginare, specialmente quando combinata con espressioni regolari.

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