酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

XMLをロードしたあとに条件が一致しているかどうかを調べて item要素を取り出す

の続きです。

@ch3cooh たびたびもうしわけないのですが、例えばの配下の要素のみを変数に代入する方法はあるのでしょうか?属性値が一致する要素のみを抜き出したいのです

LINQを使いましょう!

XML内にのものが複数個あるのを想定されているのであれば、Whereメソッドで条件に一致するitem要素を全て取得することが出来ます。

得られる型がIEnumerable型になっているので、foreach等で欲しい情報を取得してください。

// item要素の属性が"2"であるもの全てを取り出す
IEnumerable<XElement> selItems
= items.Where(el => el.Attribute("no") != null && el.Attribute("no").Value == "2");
foreach (var item in selItems) {
    var name = item.Element("name").Value;
    System.Diagnostics.Debug.WriteLine(name);
}

先ほどIEnumerable型で複数個取ったうち、一番最初のitem要素を取り出しています。一番最後のitem要素を取得したい場合は、Firstメソッドの代わりにLastメソッドを使ってください。

    // item要素の属性が"2"のうち最初のものを取り出す
    XElement item = items.Where(el => el.Attribute("no") != null && el.Attribute("no").Value == "2").First();
    var name = item.Element("name").Value;
    System.Diagnostics.Debug.WriteLine(name);

条件に一致する要素が1件しかないと決まっている場合は、Singleメソッドで取り出すのも良いかもしれません。

取得時に結果が1件では無い場合に例外が発生しますので、上手く例外を拾って対応してください。

    // item要素の属性が"2"であるものを1つだけ取り出す
    XElement selectItem = null;
    try {
        selectItem = items.Single(item => item.Attribute("no") != null && item.Attribute("no").Value == "2");
    } catch {
        // 1つもない場合、2つ以上ある場合は例外が発生する
    }
    var name = selectItem.Element("name").Value;

10/17 追記

MS MVP for VC#の@neuecc先生に教えて頂きました。

テストに使ったのは以下のXMLテキストです。

string xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" 
  + "<items><item><name>割りと普通</name></item>"
  + "<item no=\"2\"><name>しばやん</name></item></items>";

な、なんだってー!「el.Attribute("no") != null」でチェックしている理由です。

全てのitem要素にno属性が含まれている場合は問題ないのですが、下記のように最初のitem要素のno属性を削った場合、el.Attribute("no")でnullが返ってきます。

結果的にnullオブジェクトのValueプロパティを参照してしまい、NullReferenceExceptionが発生します。それを回避する為に属性の値を取得する前に「el.Attribute("no") != null」で、no属性が存在しているかどうかをチェックしていました。

// item要素の属性が"2"であるもの全てを取り出す
IEnumerable<XElement> selItems
    = items.Where(el => (string)el.Attribute("no") == "2");

おぉ、コードがシンプルになった……さらにno属性をint型で比較したい場合についてのテクニックも教えてもらえました。


??演算子を使って、no属性が無い場合に0として扱い、int型の2と比較しています。

// item要素の属性が"2"であるもの全てを取り出す
IEnumerable<XElement> selItems
    = items.Where(el => ((int?)el.Attribute("no") ?? 0) == 2);

今までのプロジェクトのコードでも、xmlを扱っている箇所は沢山あるので、もう少しシンプルなコードが書けそうです。