How to iterate through XML in Powershell?
Asked Answered
A

2

43

I have this XML document in a text file:

<?xml version="1.0"?>
<Objects>
  <Object Type="System.Management.Automation.PSCustomObject">
    <Property Name="DisplayName" Type="System.String">SQL Server (MSSQLSERVER)</Property>
    <Property Name="ServiceState" Type="Microsoft.SqlServer.Management.Smo.Wmi.ServiceState">Running</Property>
  </Object>
  <Object Type="System.Management.Automation.PSCustomObject">
    <Property Name="DisplayName" Type="System.String">SQL Server Agent (MSSQLSERVER)</Property>
    <Property Name="ServiceState" Type="Microsoft.SqlServer.Management.Smo.Wmi.ServiceState">Stopped</Property>
  </Object>
</Objects>

I want to iterate through each object and find the DisplayName and ServiceState. How would I do that? I've tried all kinds of combinations and am struggling to work it out.

I'm doing this to get the XML into a variable:

[xml]$priorServiceStates = Get-Content $serviceStatePath;

where $serviceStatePath is the xml file name shown above. I then thought I could do something like:

foreach ($obj in $priorServiceStates.Objects.Object)
{
    if($obj.ServiceState -eq "Running")
    {
        $obj.DisplayName;
    }
}

And in this example I would want a string outputted with SQL Server (MSSQLSERVER)

Anticlerical answered 29/8, 2013 at 11:10 Comment(1)
Your XML appears to be serialized directly from in memory objects. Is there a reason you wouldn't just deserialize them back into memory?Verada
G
55

PowerShell has built-in XML and XPath functions. You can use the Select-Xml cmdlet with an XPath query to select nodes from XML object and then .Node.'#text' to access node value.

[xml]$xml = Get-Content $serviceStatePath
$nodes = Select-Xml "//Object[Property/@Name='ServiceState' and Property='Running']/Property[@Name='DisplayName']" $xml
$nodes | ForEach-Object {$_.Node.'#text'}

Or shorter

[xml]$xml = Get-Content $serviceStatePath
Select-Xml "//Object[Property/@Name='ServiceState' and Property='Running']/Property[@Name='DisplayName']" $xml |
  % {$_.Node.'#text'}
Gloriane answered 29/8, 2013 at 11:28 Comment(5)
I've added some explanation.Gloriane
You can also navigate the document using the familiar property syntax: ($xml.Objects.Object | ? { $_.ServiceState -eq "Running" }).DisplayNameMacbeth
The explicit conversion to [xml] did it for me. Without the conversion, it returns the xml (as string) and the parsing does not work. No error - of course.Trencherman
What version of Powershell was this introduced in?Tananarive
Get-Content is the wrong way to load XML, because it doesn't respect the encoding attribute of the XML header. Most of the time we get lucky, only because many XML documents are UTF-8 encoded, which happens to be the default encoding used by Get-Content nowadays. Still it's wrong, for more details see https://mcmap.net/q/390909/-how-to-load-or-read-an-xml-file-using-convertto-xml-and-select-xmlJustness
P
2

You can also do it without the [xml] cast. (Although xpath is a world unto itself. https://www.w3schools.com/xml/xml_xpath.asp)

$xml = (select-xml -xpath / -path stack.xml).node
$xml.objects.object.property

Or just this, xpath is case sensitive. Both have the same output:

$xml = (select-xml -xpath /Objects/Object/Property -path stack.xml).node
$xml


Name         Type                                                #text
----         ----                                                -----
DisplayName  System.String                                       SQL Server (MSSQLSERVER)
ServiceState Microsoft.SqlServer.Management.Smo.Wmi.ServiceState Running
DisplayName  System.String                                       SQL Server Agent (MSSQLSERVER)
ServiceState Microsoft.SqlServer.Management.Smo.Wmi.ServiceState Stopped
Pernick answered 22/1, 2019 at 17:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.