.NET3.5で大きく改善されたプログラミングモデルの1つは データの検索 を第1級プログラミング概念にしたことです。この検索プログラミングモデルを総称して"LINQ"と呼んでいます。これは、.NET Language Integrated Query(.NET言語統合クエリ)の略になります。
LINQはリッチな拡張性のあるモデルをサポートしており、データソースに対して効率的なドメイン特有のプロバイダが作成できるようにします。.NET 3.5はオブジェクト、XML、データベースをサポートするLINQを有効にするライブラリがビルドインされて出荷されます。
LINQ to XMLとは?
LINQ to XMLは、.NET 3.5の"System.Xml.Linq"名前空間で実装されているビルドインのLINQデータプロバイダです。
LINQ to XMLは、XMLデータの読み込み、構築、書き込みを可能にするクリーンなプログラミングモデルを提供します。LINQ to XMLを使って、ファイルシステムやリモートのHTTP URL、またはWebサービスやインメモリのXMLコンテンツなどにあるXMLファイルに対してLINQクエリを実行することができます。
LINQ to XMLだと、今日の.NETにある低レベルなXmlReader/XmlWriter APIに比べ、各段にリッチ(で簡単)な検索やデータ形成のサポートを行うことができます。またXmlDocumentが提供するDOM APIに比べると、結果的により効率的になります。(そしてメモリの使用量が少なくなります。)
ローカルのXMLファイルを検索するためにLINQ to XMLを使用
LINQ to XMLがどの様に動作するかをつかむために、以下の様に簡単なXMLファイルをローカルのファイルシステム上に作って、それがRSSフィードを保存するように定義したカスタムのスキーマを使うようにします。
そして、新しいXDocumentクラスをSystem.Xml.Linq名前空間内で使用すれば、上記のXMLドキュメントを開いて検索することができます。特に、XMLファイルの<Feed>要素をフィルタリングして、一連の無効化されていないRSSフィード(無効化されたフィードとは、"disabled"の値が設定された"status"属性を持つ<Feed>要素。)を返したいと思っています。これを以下のコードにより実現することができます。
VB:
C#:
上記のコードスニペットをご覧頂ければ、どのように静的メソッドのXDocument.Load(path)(XDocumentオブジェクトを返します。)を使って、XMLファイルをロードしているかご確認頂けると思います。ASP.NET内でこのコードを走らせているため、ヘルパーメソッドのServer.MapPath(path)を使用して、現在コードを走らせているページに対するXMLファイルの正確な相対パスを取得しています。
一度XMLファイルに対してXDocumentオブジェクトを取得すると、必要なXMLデータを取得するためにLINQクエリ文を書くことが出来るようになります。上記のコードでは、XMLファイル内で各<Feed>要素を検索しています。これはLINQクエリ文にあるこのオープニング節で制御されます。
from feed in feedXML.Decedents("Feed")
次に、"status"属性もしくは"disabled"に設定された"status"属性を持たない"Feed"要素だけを返すフィルタを適用しています。
Where (feed.Attribute("status") Is Nothing) OrElse (feed.Attribute("status").Value <> "disabled")
そしてどの返り値が必要かを示したLINQ文でSelect節を使用しています。もし単に"select feed"と書いた場合、LINQ to XMLはフィルタに合致した各XML要素ノードを表す一連のXElementオブジェクトを返してくることになります。しかし、上記のコードサンプルでは、直接新しい匿名タイプを定義する代わりにLINQの形成/予測機能を使っており、その上に2つのプロパティ(NameとFeed)を定義しています。それらプロパティには各<Feed>要素の配下にある<Name>と<Url>のサブ要素からデータを格納しようとしています。
Select Name = feed.Element("Name").Value, Url = feed.Element("Url").Value
上記(または下記)を見て頂ければお分かりかと思いますが、この一連のデータの返り値をまるで.NETのコレクションや配列の様に扱うことができます。VS2008ではこの一連の匿名タイプに対して完全なインテリセンスとコンパイルチェックのサポートを提供しています。
また、ASP.NET、Windowsフォーム、またWPF上のどのUIコントロールに対しても結果をデータバインドすることができます。例えば、あるページにドロップダウンリストコントロールが定義されていると仮定します。
それに対して、以下のLINQ to XMLコードを使って結果をデータバインドすることができます。
そうすることで、HTMLページ上にかっこいいドロップダウンリストが作り出されます。
うーん。。 "匿名タイプ"って何?
上記のコードでは、"匿名タイプ"と呼ばれるVBおよびC#の新言語機能を活用しています。匿名タイプにより、開発者は、タイプの形式的なクラス宣言を明示的に定義することなく、コード内に簡潔なインラインのCLRタイプを定義することができます。詳細については、前投稿のNew "Orcas" Language Feature: Anonymous Types をご確認ください。
ローカルでデータを回して処理したい場合には匿名タイプは非常に有用的なのですが、複数のクラス、クラスライブラリのアセンブリ、Webサービスの間でLINQクエリの結果を引き渡したい時、大体の場合において標準クラスを定義する必要があります。
これを有効にするために、Feedデータを表示するための"FeedDefinition"という非匿名クラスを定義します。
上記を見て頂いて、プロパティを定義するために(それらに対してフィールドを定義しないで済むように)、どのようにC#の新しい"自動プロパティ" 機能を使っているかをご確認ください。
そして以下のメソッドを書いて、FeedDefinitionオブジェクトが含まれたジェネリックベースのList<FeedDefinition>コレクションが返るようにします。
上記をご覧頂くと、前に使用していたLINQ to XMLに今回加えた変更が、"select new"(タイプ名無しで)から"select enw FeedDefinition"とした"select"節だけであることがご確認頂けると思います。この変更により、一連のFeedDefinitionオブジェクトを返しているため、これをクラスからクラス、アセンブリからアセンブリ、そしてWebサービスをまたいで引き渡すことができます。
リモートのRSS XMLフィードを取得するためにLINQ to XMLを使用
静的メソッドのXDocument.Load(path)により、ファイルシステムおよびHTTP URLから返されたリモートXMLフィードの両方からXMLファイルを開くことができます。またリモートのRSSフィード、REST API、その他のWeb上に公開されたXMLフィードにアクセスすることも可能になります。
この例として、私のブログのRSS フィード (http://weblogs.asp.net/scottgu/rss.aspx)を見てみましょう。
上記のブログ投稿データを私のRSSフィードから取得するために、以下のようなLINQ to XMLコードを書き、.NETオブジェクトとして各フィード項目を処理することができます。
RSSフィールドにある"Published"フィールド(XMLでは文字列)をどのように.NETのDateTimeオブジェクトに変換しているかを上記でご確認ください。またどのようにLINQ to XMLにビルドインの(XML名前空間を宣言して処理するタイプセーフな方法を提供する)XNamespaceタイプが含まれているかもご確認頂けると思います。(これは、私が<slash:commments>要素を取得するために行う必要があります。)
これによりLINQの構成機能を活用することができ、結果に対してさらにサブクエリを実行することができるようになりますので、以下のコードを使用して過去7日間に公開されたRSS投稿だけを取得することができます。
上記を見てお分かりの通り、1つのLINQクエリ文の結果を、別のLINQ文の入力としてフィードすることができます。これにより、非常にクリーンでかなり構成がしやすいコードを書くことができます。
LINQ to XMLクエリ文でLINQ サブクエリを使用
RSSフィードの生のXMLを見て頂ければ、各投稿に対して"tag"コメントが、各<item>要素の直下に<category>要素として反復されて保存されていることがお分かり頂けると思います。
"BlogEntry"クラスに対してオブジェクトモデルを設計する時、文字列のサブコレクションとしてこれらの<category>の値を表示したい場合があります。例えば、stringタイプのジェネリックリストである"Tags"プロパティを使用します。
どのように<item>要素のフラットなコレクションを取得して、文字列のネスト化されたサブコレクションに変換するのか、不思議に思われるかも知れません。LINQのいいところは、ネスト化されたLINQクエリ文を使えるようにすることでこのようなシナリオを簡単にします。
このLINQの"形成"力、そしてそのフラットデータ構造の取得とそれらを階層型(階層データを取得してそれらをフラットにする)にする能力は、非常に強力です。この機能はデータソース(XML、SQL、平素な古いオブジェクト・配列・コレクションのいずれであっても)のどのタイプにも使用することができます。
シンプルなRSSフィードリーダーで全て一緒に
上記のチュートリアルにあるコードスニペットでは、どのように簡単にLINQ to XMLコードを書いて、ローカルのXMLファイルからRSSフィードのリストを取得することができるのか、またどのようにリモートでRSSフィードを検索して各フィードの詳細や各投稿コンテンツの項目を取得することができるのかについてご紹介しました。お分かりかもしれませんが、それにより取得結果フィードのコンテンツをASP.NETのGridViewやListViewコントロールにデータバインドして、きれいなブログフィードのビューを提供することができます。
これらの全てスニペットを一緒にして簡単なサンプルアプリケーションを構築しました。これはLINQ to XMLと新しい<asp:LIstView>コントロールで簡単なRSSリーダーをを提供します。これはここからダウンロードすることができます。ダウンロードするアプリケーションはVBとC#の両方のバージョンが含まれています。
まとめ
LINQ to XMLは効率的なクエリ、フィルタ、XMLデータの形成・変換を行う非常に強力な方法を提供します。ローカルのXMLコンテンツ、そしてリモートのXMLフィードに対しても使用することができます。これを使用するとXMLデータをアプリケーションで更に操作や転送することができる.NETオブジェクトやコレクションに簡単に変換することができます。
LINQ to XMLはデータを検索する時に、LINQ to SQL、LINQ to Objects、LINQ to SharePoint、LINQ to Amazon、LINQ to NHibernateなどが使用しているのと同じコアのLINQクエリシンタックスを使用しています。それをサポートするためにVBやC#に追加されたLINQクエリシンタックスやそれがサポートしている言語機能について、過去の私の投稿でご参照頂けます。
LINQ to SQLについても過去の私のブログ投稿でご確認頂けます。
今後のブログでまたLINQ to XMLに戻って、XMLを検索するだけでなく、非常にクリーンなXML出力を.NETデータ構造から生成する方法をご紹介したいと思います
Hope this helps,
Scott