xmlpowershell

Powershell howto Filter XML Nodes


I'm searching for a way to filter XML-Files. I couldn't find a good tutorial, so I ask here: for that here a small exmaple XML:

<?xml version="1.0" encoding="utf-8"?>
<project>
    <AdditionalInformation>
        <Attribute Name="YEAR" AttributeDataType="xs:int">
            <Value>2024</Value>
        </Attribute>    
    
    </AdditionalInformation>
    <element ID="123546" Name="axis">
        <Attribute Name="Axe-Info" AttributeDataType="xs:string">
            <Value>Rack</Value>
        </Attribute>
        <element ID="58655" >   
            <Attribute Name="level" AttributeDataType="xs:string">
                <Value>X</Value>
            </Attribute>
            <Attribute Name="direction" AttributeDataType="xs:string">
                <Value>left</Value>
            </Attribute>
            
        </element>
                <element ID="58658" >   
            <Attribute Name="level" AttributeDataType="xs:string">
                <Value>Y</Value>
            </Attribute>
            <Attribute Name="direction" AttributeDataType="xs:string">
                <Value>right</Value>
            </Attribute>
            
        </element>
        <element ID="58685" >   
            <Attribute Name="level" AttributeDataType="xs:string">
                <Value>Z</Value>
            </Attribute>
            <Attribute Name="direction" AttributeDataType="xs:string">
                <Value>left</Value>
            </Attribute>
        </element>
        <element ID="586888" >  
            <Attribute Name="angle" AttributeDataType="xs:string">
                <Value>horizontal</Value>
            </Attribute>
            <Attribute Name="position" AttributeDataType="xs:int">
                <Value>185</Value>
            </Attribute>
        </element>
        <element ID="5899858" > 
            <Attribute Name="angle" AttributeDataType="xs:string">
                <Value>vertical</Value>
            </Attribute>
            <Attribute Name="position" AttributeDataType="xs:int">
                <Value>152</Value>
            </Attribute>
        </element>
    </element>
    <element ID="128546" Name="engine">
        <Attribute Name="Eng-Info" AttributeDataType="xs:string">
            <Value>Rack</Value>
        </Attribute>
        <element ID="5865855" > 
            <Attribute Name="power" AttributeDataType="xs:int">
                <Value>400</Value>
            </Attribute>
            <Attribute Name="unit" AttributeDataType="xs:string">
                <Value>kW</Value>
            </Attribute>
        </element>
        <element ID="5865155" > 
            <Attribute Name="power" AttributeDataType="xs:int">
                <Value>200</Value>
            </Attribute>
            <Attribute Name="unit" AttributeDataType="xs:string">
                <Value>kW</Value>
            </Attribute>
        </element>
    </element>


</project>

In End I'd like to have the following Information:

x,left Y,right Z,left

For That I've to Filter first the element-node with the Name "axis". Then I have to filter alle element-nodes which have an attribute-Node with the Name "level"

How can I do that filtering operations in Powershell?

Thanks


Solution

  • Perhaps something like this, find all nodes with Attribute Name equal to level and from there look for their next sibling to get the direction value, so:

    $xml = [xml]::new()
    $xml.Load('path\to\theXML.xml')
    $xml.SelectNodes("//*[@Name='level']") | ForEach-Object {
        [pscustomobject]@{
            Level     = $_.Value
            Direction = $_.NextSibling.Value
        }
    }
    

    This would produce this output using the sample XML:

    Level Direction
    ----- ---------
    X     left
    Y     right
    Z     left
    

    Another approach a bit more hacky, this would produce array of strings instead of objects:

    $i = $true
    $xml.SelectNodes("//*[@Name='level' or @Name='direction']").Value |
        ForEach-Object { if ($i = -not $i) { "$prev, $_" }; $prev = $_ }
    
    # X, left
    # Y, right
    # Z, left