2016-05-19 3 views
1

Я хотел бы извлечь из xml-файла значение атрибутов name из всех оконных и всех точек зрения.Вложенный цикл через xml-узлы using powershell

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

Упс, вы правы - я исправил свой ответ (теперь используя свойства ParentNode для получения панели). Я также предоставил вам пример без xpath за минуту. –

+0

Хмм, строка $ worksheets = $ doc.SelectNodes ("// window [@class = 'dashboard'] // viewpoint [@name]") вызывает ошибку. (перевод с немецкого языка: невозможно, чтобы вызвать метод выражения, имеющего значение NULL) – Nylix

Смежные вопросы