2016-05-19 38 views
1

我想从xml文件中提取所有窗口和所有视点节点的名称属性的值。使用PowerShell通过xml-节点嵌套循环

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

这是我写的代码:

[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 

我期待什么:

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

我得到了什么:

 
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 

这里有什么问题?结果与我对嵌套循环如何工作的理解完全矛盾。

回答

2

/字符开头的XPath表示它将从文档根节点开始。要从上下文节点创建相对XPath,您需要在/之前放置.

所以,你的代码应该是:

[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 
+0

您节省了我的一天。谢谢。我花了好几个小时才发现错误,只有一点点缺失...... – Nylix

1

你也可以简化并采用您的xpath选择从仪表盘窗口中的所有viewpoint节点:

[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 

在这里,一个解决方案,而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} 
    } 
} 
+0

现在没有定义'$ dashboard',对吧? – har07

+0

Upps,你是对的 - 我修复了我的答案(现在使用'ParentNode'属性来获取仪表板)。我也会在一分钟内为你提供一个没有xpath的例子。 –

+0

嗯,行$ worksheets = $ doc.SelectNodes(“// window [@class ='dashboard'] // viewpoint [@name]”)会产生一个错误。 (德语翻译:这是不可能的,为值为NULL的表达式调用方法) – Nylix