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にそのままキャストできるとのコメントを頂きました。ありがとうございます。
全然知りませんでした。
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でやってるみたいです。
間違っていたらすみません。最初の例ですが、
string value = (string)e.Attribute("something").Value;
とキャストするとうまくいきませんか?
stringはnull許容するのでキャストが効きます。intなどの場合はNull許容型であるint?を使うといいと思います。
参考) http://msdn.microsoft.com/ja-jp/library/vstudio/bb387049.aspx
間違ってました。.Value は不要です。
×string value = (string)e.Attribute("something").Value;
○string value = (string)e.Attribute("something");
コメントありがとうございます。
ダイレクトにキャスト出来るんですね!とても良いことを聞きました。
記事にも反映させていただきました。