DOBON.NET DOBON.NETプログラミング掲示板過去ログ

xelement を remove するための foreach

環境/言語:[win7 visual studio 2008 c#]
分類:[.NET]

こんにちは、xelementで管理しているデータで、
特定の条件のものを一度に削除しようとしています。

foreachを使って.remove()をしてみたのですが、
最初のelementを削除できるものの、そこでforeachが終了してしまいます。


foreachを使うのが間違いなのか、何か手順を間違っているのか、
教えて頂けると嬉しいです。


foreach ( XElement 削除対象 in xml対象者一覧 ){
    if (bool.Parse(削除対象.Element("Remove").Value))
    {
        削除対象.Remove();
    }
}


データは次のようになっています。
<root>
  <User>
    <UserId>86462918</UserId>
    <DateTime>2010-09-01T03:23:01.3054427+09:00</DateTime>
    <Remove>true</Remove>
  </User>
  <User>
    <UserId>128825055</UserId>
    <DateTime>2010-09-01T03:23:01.3054427+09:00</DateTime>
    <Remove>true</Remove>
  </User>
  <User>
    <UserId>6690262</UserId>
    <DateTime>2010-09-01T03:23:01.3054427+09:00</DateTime>
    <Remove>true</Remove>
  </User>
</root>
foreach による列挙は、列挙中に列挙しているコレクションを変更する操作はできません。
ですので、先に一旦 List<T> など別コレクションに子要素をコピーし、
そのコレクションを列挙して元のコレクションから削除する、という手順になります。
幸い、Linq では Enumerable クラスに ToList という便利な拡張メソッドがあります。
コレを呼び出すだけで List<T> へのコピーが作れます。

あと、
> bool.Parse(削除対象.Element("Remove").Value)
XElement はキャスト式により bool に変換できます。
(bool)削除対象.Element("Remove")
のように(要素がないことがありうる場合、bool? にキャストすることも可能。空要素には対応できないのが難ですが)。
Hongliangさま

こんにちは、今村です。

回答ありがとうございます。

まだまだ理解不足でどのようなソースを書けばよいか
判らない状況です。

「先に一旦 List<T> など別コレクションに子要素をコピーし、
そのコレクションを列挙して元のコレクションから削除する、
という手順になります。」

という部分がよくわかりません。

大変お手数ですが、参考ソースを教えて頂けないでしょうか。
よろしくお願いします。
Hongliangさま

先ほどサンプルが欲しいとお願いしたのですが、
サイト上で見つけることが出来ました。

http://msdn.microsoft.com/ja-jp/library/bb387088%28v=VS.90%29.aspx

XElement root = new XElement("Root",
    new XElement("A", "1"),
    new XElement("B", "2"),
    new XElement("C", "3")
);
foreach (XElement e in root.Elements().ToList())
    e.Remove();
Console.WriteLine(root);

を参考に直したところ無事に動くようになりました。

ありがとうございます!
■No27387に返信(imamuraさんの記事)
> まだまだ理解不足でどのようなソースを書けばよいか
> 判らない状況です。

var xml = XDocument.Parse(@"<root>
  <User>
    <UserId>86462918</UserId>
    <DateTime>2010-09-01T03:23:01.3054427+09:00</DateTime>
    <Remove>true</Remove>
  </User>
  <User>
    <UserId>128825055</UserId>
    <DateTime>2010-09-01T03:23:01.3054427+09:00</DateTime>
    <Remove>true</Remove>
  </User>
  <User>
    <UserId>6690262</UserId>
    <DateTime>2010-09-01T03:23:01.3054427+09:00</DateTime>
    <Remove>true</Remove>
  </User>
</root>");


var xml対象者一覧 = xml.Descendants("User");
foreach (XElement 削除対象 in xml対象者一覧.ToList())
{
    if ((bool)削除対象.Element("Remove")) 削除対象.Remove();
}

----------
もしくは:

var xml対象者一覧 = xml.Descendants("User");
xml対象者一覧.Where(n => (bool)n.Element("Remove")).Remove();
既に解決してましたか。^^;
まだ[解決済み]がマークされていないので追記で。


■No27390に返信(魔界の仮面弁士に追記)
> もしくは:
> var xml対象者一覧 = xml.Descendants("User");
> xml対象者一覧.Where(n => (bool)n.Element("Remove")).Remove();

あるいは:

// using System.Xml.XPath;
xml.XPathSelectElements("/root/User[Remove='true']").Remove();
魔界の仮面弁士さん、ありがとうございます。

>>var xml対象者一覧 = xml.Descendants("User");
>>xml対象者一覧.Where(n => (bool)n.Element("Remove")).Remove();
>
> あるいは:
>
> // using System.Xml.XPath;
> xml.XPathSelectElements("/root/User[Remove='true']").Remove();

こういう書き方もあるんですね。
勉強になります。

XElementとLinqはいつも頭を抱えてしまいます。
使いこなせるようになるとスゴイ便利だと思うので、
これからも勉強していきます。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板