既にいくつかの機能を追加してしまったので、実装は先に進んでいるのですが、ちょっと文字列(string)絡みのところを。
// ルート要素を取得する EXElement el = doc / "members"; // person 要素を取得する EXElements els = doc / "members" / "person";
このように、きっちりと型に保存してもいいのですが、最近の流行は型推論ですよね。
なので、C# 3.0 の場合は、var を使って
// ルート要素を取得する var el = doc / "members"; // person 要素を取得する var els = doc / "members" / "person";
のようにしてもいいのですが、今回は【型推論】のアプローチじゃなくて、【厳密な型宣言】を応用していきます。つまりは、暗黙のキャスト(implicit)を使っているように見えて、変換先の型が振る舞いを変えている、という受け側の【型】が重要になるというアプローチです。
型推論の場合は、最初に型が決定していなくて後から決定する場合でも OK ってな感じですが、自分が使いやすい型に当てはめる形で値を受け取る、という違うアプローチなのですね…って、本当に違うのかどうか、よく分からないのですが、なんとなく対照性があるかなぁと。
さて、次のコードを見てください。
// ルート要素を取得する string val = doc / "members"; // person 要素を取得する string[] vals = doc / "members" / "person";
この場合、string と sting[] 型が明示的に使われています。members 要素の値を文字列で受け取りたい、複数ある person 要素の値を文字列の配列で受け取りたい、という【意図】がこの時点で明確になります。
ここで var の場合を使ってしまうと、この時点では、string に入れようとしているのか、EXElement に入れようとしているのか分かりません。
こんな風に、【型を厳密に】指定することによるメリットが大きいわけです。
# 当然ですが、この技は Ruby や PHP などの型がないスクリプト言語では使えません。もし、型のあるスクリプト言語があれば(私はよく知らないのですが)、このアプローチが取れます。
更に、不思議なアプローチを紹介しましょう。
// person 要素を配列で取得する string[] vals = doc / "members" / "person"; // person 要素をひとつ取得する string val = doc / "members" / "person";
この例では、doc/members/person の配列あるい文字列を取得します。
同じように指定しているのに、配列で取れたり、文字列だけで取れたりするのは不思議でしょう?
でも、この書き方は XML を解析する側としては、明白なことでもあるのです。
XML からデータを取得する場合、あらかじめ XML のデータ構造が分かっていることが多いのです。DTD を指定されないとしても、大まかな構造が分かっている場合がほとんどでしょう。
そうなれば、/members/person が単数なのか、複数なのかは、プログラミングをするときに決まっていますよね。
なので、どうせ XML 構造を把握しているのであれば、それを利用して、単数の string も同様に取れるようにしてしまうほうが自然です。
# たとえば、ファミレスの店員が、ひとりのお客を目の前にして「ただいま、20名のお客様は入れません」と言うことはない…訳でもないけれど、ほぼ無いですよね。まずは「おひとりでしょうか?」と尋ねるのが自然です。
なので、単数で取るのか、配列で取るのかをプログラミングしているときに分けるのも自然です。っていうのが、このコードの主旨です。
と、長々と書きましたが、実はコードが非常に短いからなのです。EXElements クラスができているので、これを string[] や string にキャストするメソッドを作ります。
public class EXElements : List<EXElement> { /// <summary> /// 文字列の配列にキャスト /// </summary> /// <param name="els"></param> /// <param name="tag"></param> /// <returns></returns> public static implicit operator string[]( EXElements els ) { List<string> items = new List<string>(); if (els.Count > 0) { foreach (EXElement el in els) { items.Add(el.Value); } } return items.ToArray(); } /// <summary> /// 文字列にキャスト /// </summary> /// <param name="els"></param> /// <returns></returns> public static implicit operator string(EXElements els) { if (els.Count == 0) { return ""; } else { return els[0].Value; } } }
馬鹿馬鹿しいほど、簡単なコードですね。implicit で暗黙にキャストを指定しておいて、キャスト先の string[] あるいは string にあわせるだけです。
さて、お次は比較演算子(==)を多重定義して、属性の値によって抽出をしてみます。