[C#]XElementから属性値を簡単に取る

XElement + Linq + XPathはすごく便利。便利だけど、Attributeを読み出そうとするとき、毎回ちょっと面倒な思いをしていた。

というのは、XElementから属性値(Attribute)を読み出すとき、その属性が省略可能であれば一度Attributeを取得して、それがnullかどうかチェックして、nullの時の処理をいちいち加えなくてはならない。

具体的に示そう。以下のようなコードはAttributeが省略可能(属性を記述しないことを許す)であった場合、場合によってはNullReferenceExceptionをスローする。XElement.Attributeメソッドは、対象が存在しない場合はnullを返すからだ。

// eはXElement
string value = e.Attribute("something").Value;

だから、こういうときは、普通、以下のようなコードを書く。

var attr = e.Attribute("something");
if (attr == null)
    return string.Empty;
else
    return attr.Value;

で、属性値が例えばboolならばParseしてやる必要がある。

var attr = e.Attribute("something");
if (attr == null)
    return string.Empty;
else
    return bool.Parse(attr.Value);

これに加えてParseできなかったときはデフォルトでfalseにして…とかやってると、もういやになる。毎回こんなことやってたらダサい。ダサすぎる。ダサいコードなんて書きたくない。ダサいコード書く人とは結婚できません。なので、拡張メソッドでごまかすことにした。

public static class Extension
{
    public static string AttributeValue(this XElement e, string attributeName)
    {
        var attr = e.Attribute(attributeName);
        if (attr == null)
            return string.Empty;
        else
            return attr.Value;
    }

    public static string AttributeValue(this XElement e, string attributeName, string defaultValue)
    {
        var attr = e.Attribute(attributeName);
        if (attr == null)
            return defaultValue;
        else
            return attr.Value;
    }

    public static T AttributeValue<T>(this XElement e, string attributeName)
    {
        var attr = e.Attribute(attributeName);
        if (attr == null)
            return default(T);
        else
            return (T)Convert.ChangeType(attr.Value, typeof(T));
    }

    public static T AttributeValue<T>(this XElement e, string attributeName, T defaultValue)
    {
        var attr = e.Attribute(attributeName);
        if (attr == null)
            return defaultValue;
        else
            return (T)Convert.ChangeType(attr.Value, typeof(T));
    }
}

使い方はこう。

// something属性がなかったら空文字が出力される
string value = e.AttributeValue("something");

// something属性がなかったらassholeといわれる
string you = e.AttributeValue("something", "asshole");

// something属性がなかったらfalseが出力される
bool baa = e.AttributeValue<bool>("something", false);

拡張メソッドなので、拡張メソッドを書いたクラスの名前空間をusingするのを忘れないようにね。

ちなみに、Convert.ChangeTypeで失敗した時の処理は何も考えてないので、必要に応じて適当に書き換えてください。ChangeTypeに失敗したらdefaultValueを返すとかという仕様でも良いのかも。

他にもっといいやり方があれば教えて下さい。

2013/05/21追記:

XAttributはstringやintやNullableなintにそのままキャストできるとのコメントを頂きました。ありがとうございます。
全然知りませんでした。

方法 : 要素の値を取得する (LINQ to XML)

XAttribute attr = new XAttribute("name", "value");
XAttribute intattr = new XAttribute("name", "123");
XElement elem = new XElement("name", "value");
int intValue = (int)intattr;
string attrString = (string)attr;
string elemString = (string)elem;
Debug.WriteLine(intValue + 1); // outputs 124
Debug.WriteLine(attrString); // outputs "value"
Debug.WriteLine(elemString); // outputs "value"

なにこれ?すごくない?どういう仕組みになってるんだろう・・・。

2013年12月20日追記:
explicitでやってるみたいです。

3件のコメント

  1. p-nix より:

    間違っていたらすみません。最初の例ですが、
    string value = (string)e.Attribute("something").Value;
    とキャストするとうまくいきませんか?
    stringはnull許容するのでキャストが効きます。intなどの場合はNull許容型であるint?を使うといいと思います。
    参考) http://msdn.microsoft.com/ja-jp/library/vstudio/bb387049.aspx

  2. p-nix より:

    間違ってました。.Value は不要です。
    ×string value = (string)e.Attribute("something").Value;
    ○string value = (string)e.Attribute("something");

    1. withpop より:

      コメントありがとうございます。
      ダイレクトにキャスト出来るんですね!とても良いことを聞きました。

      記事にも反映させていただきました。

ただいまコメントは受け付けていません。