我有一个循环,可以将XML Elements从一个位置移动到另一个位置。它适用于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" />
...
答案 0 :(得分: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)"