1

I have the following xml:

<?xml version="1.0"?>
<AdminDirectorUsers>
    <Users>
        <User>
            <UserName>PC1</UserName>
            <Installation>
                <Products>
                    <Product>
                        <ProductID>Product1</ProductID>
                        <Selected>Yes</Selected>
                    </Product>
                    <Product>
                        <ProductID>Product2</ProductID>
                        <Selected>No</Selected>
                    </Product>
                    <Product>
                        <ProductID>Product3</ProductID>
                        <Selected>No</Selected>
                    </Product>
                    <Product>
                        <ProductID>Product4</ProductID>
                        <Selected>Yes</Selected>                        
                    </Product>
                </Products>
            </Installation>
        </User>
        <User>
            <UserName>PC2</UserName>
            <Installation>
                <Products>
                    <Product>
                        <ProductID>Product1</ProductID>
                        <Selected>Yes</Selected>
                    </Product>
                    <Product>
                        <ProductID>Product2</ProductID>
                        <Selected>Yes</Selected>
                    </Product>
                    <Product>
                        <ProductID>Product3</ProductID>
                        <Selected>No</Selected>
                    </Product>
                    <Product>
                        <ProductID>Product4</ProductID>
                        <Selected>Yes</Selected>                        
                    </Product>
                </Products>
            </Installation>
        </User>
    </Users>
</AdminDirectorUsers>

I would like to loop through and extract a list of each UserName with all ProductIDs that has a sibling node Selected with a value of Yes, while ignoring the values of No.

$path = 'C:\Temp\test.xml'
$xml = New-Object -TypeName XML
$xml.Load($Path)
$UserNode = $xml.SelectNodes('//User')
$ProductNode = $xml.SelectNodes('//Product')

foreach ($PC in $usernode)
{
  $PC.username
  foreach($Node in $ProductNode){
    # Test if selected == 'yes'
    if($Node.Selected -eq 'yes') {
      $Node.ProductID
    }
  }
}

My output is:

PC1
Product1
Product4
Product1
Product2
Product4
PC2
Product1
Product4
Product1
Product2
Product4

...but I'm looking for:

PC1
Product1
Product4
PC2
Product1
Product2
Product4

I've tried many ways to do this but nothing seems to work. What am I missing?

2 Answers 2

1

Change the second XPath expression used for the Product/ProductID to only get the descendants of a specific <User> element, by anchoring against . (the "current node"):

# Enumerate each `<User>` element
$xml |Select-Xml '//User' |%{
  # Grab the exact username value
  $username = $_.Node.UserName

  # Then enumerate all descendant `<ProductID>` nodes who has a sibling `<Selected>Yes</Selected>` and attach username
  $_.Node |Select-Xml './/ProductID[../Selected[. = "Yes"]]' |Select -Expand Node |Select @{Name='Username';Expression={$username}},InnerText
}

(I'm using Select-Xml here but this works the same with the SelectNodes() method)

Sign up to request clarification or add additional context in comments.

2 Comments

This was driving my crazy! Thank you, I could not figure out how to anchor it to get only the descendants.
@strayletters You're most welcome! Note that you can describe the relative position in the path the same way you would with the file system (. describes "this node", .. describes the immediate parent)
1

Try to use this code:

Clear-Host
[XML]$Data = Get-Content 'C:\temp\test.xml'
[array]$UsersData = $Data.AdminDirectorUsers.Users.User
foreach($PC in $UsersData) {
    $PC.username
    foreach($Node in $PC.Installation.Products.Product){
        if($Node.Selected -eq 'Yes') {
            $Node.ProductID
        }
    }
}

My result with your xml file is

PC1
Product1
Product4
PC2
Product1
Product2
Product4

1 Comment

This works great too! That was the output I was asking for. I never thought of using an array like that. Handy to keep in my back pocket.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.