0

XML要素をある場所から別の場所に移動するループがあります。Forループでは正常に機能しますが、Foreachループでは永久に繰り返されます。これは実装の不幸な衝突(XMLElementと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')

...ソース内のファイルノードのみを選択します。(結局のところ、宛先にあるものはすでに必要な場所にあるため、含める必要はありません。)この変更により、コードは期待どおりに実行されます。

これが機能する別のバリ​​エーションで、元のセレクターに戻ります。オクトソープ(#############)でマークされた3行のみを変更してノードを取得し、別々の手順で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 に答える