1

I would like to extract from a xml-file the value of the name-attributes from all window- and all viewpoint-nodes.

<?xml version='1.0' encoding='utf-8' ?>
        <workbook>
            <windows>
                <window class='dashboard' name='D1'>
                  <viewpoints>
                    <viewpoint name='V1'> </viewpoint>
                    <viewpoint name='V2'> </viewpoint>
                    <viewpoint name='V3'> </viewpoint>
                </viewpoints>
                </window>
                <window class='dashboard' name='D2'>
                  <viewpoints>
                    <viewpoint name='V10'> </viewpoint>
                    <viewpoint name='V11'> </viewpoint>
                  </viewpoints>
                 </window>
            </windows>
        </workbook>

This is the code I have written:

[XML]$doc = get-content -path 'W:\Demo1.xml'


$objs = @()
$dashboards = $doc.SelectNodes("//window[@class = 'dashboard']")

foreach ($dashboard in $dashboards)
{
   $worksheets = $dashboard.SelectNodes("//viewpoint[@name]")

    foreach ($worksheet in $worksheets)
    {
        $obj = new-object psobject -prop @{Dashboard=$dashboard.name; Worksheet = $worksheet.name}; 
        $objs += $obj;

    }  
 } 

$objs

What I expected:

dashboard   worksheet   
D1          V1
D1          V2
D1          V3
D2          V10
D2          V11

What I got:

dashboard   worksheet   
D1          V1
D1          V2
D1          V3
D1          V10
D1          V11
D1          V14
D2          V1
D2          V2
D2          V3
D2          V10
D2          V11
D2          V14

What is wrong here? The result is completely contradictory to my understanding of how a nested loop works.

2 Answers 2

2

An XPath starting with a / character means that it will be begin at the document root node. To create a relative XPath from the context node, you need to put a . before the /.

So your code should be:

[XML]$doc = get-content -path 'W:\Demo1.xml'


$objs = @()
$dashboards = $doc.SelectNodes("//window[@class = 'dashboard']")

foreach ($dashboard in $dashboards)
{
   $worksheets = $dashboard.SelectNodes(".//viewpoint[@name]")

    foreach ($worksheet in $worksheets)
    {
        $obj = new-object psobject -prop @{Dashboard=$dashboard.name; Worksheet = $worksheet.name}; 
        $objs += $obj;

    }  
 } 

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

1 Comment

You saved my day. Thank you. I spend hours to find the error and it's only a little dot missing......
1

You could also simplify that and adopt your xpath to select all viewpoint nodes from dashboard window:

[XML]$doc = get-content -path 'W:\Demo1.xml'

$objs = @()
$worksheets = $doc.SelectNodes("//window[@class = 'dashboard']//viewpoint[@name]")

foreach ($worksheet in $worksheets)
{
    $obj = new-object psobject -prop @{Dashboard=$worksheet.ParentNode.ParentNode.name; Worksheet = $worksheet.name}; 
    $objs += $obj;
}  

$objs

And here a solution without xpath:

[XML]$doc = get-content -path 'W:\Demo1.xml'

$objs = $doc.DocumentElement.windows.window | Where class -eq 'dashboard' | foreach {
    $dashboardName = $_.name
    $_.viewpoints.viewpoint | foreach {
        new-object psobject -prop @{Dashboard=$dashboardName; Worksheet = $_.name}
    }
}

4 Comments

Upps, you are right - I fixed my answer (now using ParentNode properties to get the dashboard). I will also provide you an example without xpath in a min.
Hmm, the line $worksheets = $doc.SelectNodes("//window[@class = 'dashboard']//viewpoint[@name]") produces an error. (translated from german: it's not possible, to invoke a method for an expression having the value NULL)
@jisaak: thank you for your second piece of code. I just tried it out. For whatever reason it returns nothing. :-(
I omit the $objs that prints the result, I only assign the result to it. Just write $objs in the last line and you will see the output.

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.