0

我有一个循环将 XML 元素从一个位置移动到另一个位置。它适用于 For 循环,但对于 Foreach 循环,它会永远迭代。这只是不幸的实现冲突(XMLElement vs foreach 循环)还是我缺少更好的实践?我绝不是 System.Xml 库的专家。

演示脚本

$xmlDocOriginalValue = [xml]@"
<root>
    <dir Id = "source" >
        <file Id = "1" />
        <file Id = "2" />
    </dir>
    <dir Id = "destination" >
        <file Id = "3" />
        <file Id = "4" />
    </dir>
</root>
"@

$xmlDoc = $xmlDocOriginalValue.Clone()
$files = $xmlDoc.SelectNodes('//file')
$destination = $xmlDoc.SelectSingleNode('//dir[@Id="destination"]')

# Move "files" from source to destination using both a "for" and a "foreach" loop

Write-Host "For loop"
Write-Host "XML before: $($xmlDoc.OuterXml)"
for($i = 0; $i -lt $files.count; $i++)
{
    Write-Host ("Iteration "+$i + ", " + $files[$i].OuterXml)
    $destination.AppendChild($files[$i]) | Out-Null
}
Write-Host "XML after: $($xmlDoc.OuterXml)"

# Reset and try with a foreach loop
$xmlDoc = $xmlDocOriginalValue.Clone()
$files = $xmlDoc.SelectNodes('//file')
$destination = $xmlDoc.SelectSingleNode('//dir[@Id="destination"]')

Write-Host "`nForeach loop"
Write-Host "XML before: $($xmlDoc.OuterXml)"
$i = 0
foreach($file in $files)
{
    Write-Host ("Iteration "+$i + ", " + $file.OuterXml)
    $destination.AppendChild($file) | Out-Null
    $i++
}
Write-Host "XML after: $($xmlDoc.OuterXml)"

输出

For loop
XML before: <root><dir Id="source"><file Id="1" /><file Id="2" /></dir><dir Id="destination"><file Id="3" /><file Id="4" /></dir></root>
Iteration 0, <file Id="1" />
Iteration 1, <file Id="2" />
Iteration 2, <file Id="3" />
Iteration 3, <file Id="4" />
XML after: <root><dir Id="source"></dir><dir Id="destination"><file Id="1" /><file Id="2" /><file Id="3" /><file Id="4" /></dir></root>

Foreach loop
XML before: <root><dir Id="source"><file Id="1" /><file Id="2" /></dir><dir Id="destination"><file Id="3" /><file Id="4" /></dir></root>
Iteration 0, <file Id="1" />
Iteration 1, <file Id="2" />
Iteration 2, <file Id="3" />
Iteration 3, <file Id="4" />
Iteration 4, <file Id="1" />
Iteration 5, <file Id="2" />
Iteration 6, <file Id="3" />
Iteration 7, <file Id="4" />
Iteration 8, <file Id="1" />
Iteration 9, <file Id="2" />
Iteration 10, <file Id="3" />
Iteration 11, <file Id="4" />
Iteration 12, <file Id="1" />
Iteration 13, <file Id="2" />
Iteration 14, <file Id="3" />
Iteration 15, <file Id="4" />
Iteration 16, <file Id="1" />
Iteration 17, <file Id="2" />
Iteration 18, <file Id="3" />
...
4

1 回答 1

0

从快速实验来看,这确实是一个冲突,在尝试在循环中修改迭代器的意义上。也就是说,您最初选择了所有元素——包括那些在目标中的元素。人们会认为这应该是无害的,但显然不是。为了证明这一点,请将您选择的节点更改为:

$files = $xmlDoc.SelectNodes('//dir[@Id="source"]/file')

...其中您仅选择源中的那些文件节点。(毕竟,不需要包含目标中的那些,因为它们已经在您想要的位置。)通过该更改,代码按预期执行。

这是另一个可行的变体,回到您原来的选择器。我只更改了标有 octothorps (#############) 的三行来获取节点,然后在单独的步骤中修改 XML:

# Reset and try with a foreach loop
$xmlDoc = $xmlDocOriginalValue.Clone()
$files = $xmlDoc.SelectNodes('//file')
$destination = $xmlDoc.SelectSingleNode('//dir[@Id="destination"]')

Write-Host "`nForeach loop"
Write-Host "XML before: $($xmlDoc.OuterXml)"

$i = 0
$list = @()                                    ##############
foreach($file in $files) { $list += $file }    ##############
foreach($item in $list)                        ##############
{ 
    Write-Host ("Iteration "+$i + ", " + $file.OuterXml)
    $destination.AppendChild($item) | Out-Null 
    $i++
}
Write-Host "XML after: $($xmlDoc.OuterXml)"
于 2012-11-29T17:34:26.310 回答