プログラミング言語とXPath(4)

.NETでLINQのXDocumentに対してXPathSelectElementsにXPath式を指定してノードを選択する例を示します.非常に単純でドキュメントノードに対してXPathSelectElementsを適用するだけです.

Imports System.Xml.Linq
Imports System.Xml.XPath.Extensions

Module Main

    Sub Main()
        Dim xd As XDocument = XDocument.Load("MusicLibrary.xml")
        Dim titles As IEnumerable(Of XElement) = xd.XPathSelectElements("musicLibrary/cd[string(year) = '1994']/title")
        For Each title As XElement In titles
            Console.WriteLine("The title is '{0}'", title.Value)
        Next
    End Sub

End Module

結果は The title is 'The Dark Side of the Moon' と表示されます.

ここで考えておかねばならないことですが、まずMicrosoftXPathの実装はXPath 1.0レベルであるということです.Javaの場合は、SaxonにXPathのモジュールを動的に変えてすぐXPath 2.0が使えましたが、.NETではサードパーティのライブラリでも使わない限りXPath 2.0は使えないです.

あと、LINQの中でのXPathの使用はLINQの機能に比べるとパフォーマンスが劣るとされています.

Performance Differences
XPath queries that use the XPath functionality in LINQ to XML will not perform as well as LINQ to XML queries.


ではMicrosoftの本命のLINQでは、このXPathの条件を実現しようとしたらどのように書けるのでしょうか?まずLINQクェリー式を使った方法は以下のようになります.

        Dim titles As IEnumerable(Of XElement) = _
            From title In xd.<musicLibrary>.<cd>.<title> _
            Where title.Ancestors().ElementAt(0).<year>.Value = "1994" _
            Select title

まさにキーワードの順番は違いますがSQLを書いているのとまったく同じ感じですね.これで同じ結果を得ることができます.このように直感的にタグでかけるのと同様に

        Dim titles As IEnumerable(Of XElement) = _
            From title In xd.Element("musicLibrary").Elements("cd").Elements("title") _
            Where title.Ancestors().ElementAt(0).Element("year").Value = "1994" _
            Select title

とも書けます.なかなか柔軟性が高いです.ここでXElementはクラスでElementは関数で別物です.

またこのようにクェリー式を書くのではなくて以下のようにしてもできます.

        Dim titles As IEnumerable(Of XElement) = xd.<musicLibrary>.<cd>.Where(AddressOf selectYear).<title>

        Function selectYear(cd As XElement) As Boolean
            Return cd.<year>.Value.Equals("1994")
        End Function

ラムダ式を使えば別個に関数を定義する必要はなくなります.VBも便利になりました.

        Dim titles As IEnumerable(Of XElement) = xd.<musicLibrary>.<cd>.Where(Function(cd) cd.<year>.Value.Equals("1994")).<title>

あとLINQを使えば、XPathが1.0しか使えないという制限を取り除くことができます.JavaのところでSaxonにやらせた正規表現の判定も次のように.NETの正規表現の関数を使用すれば実現できます.

        Imports System.Text.RegularExpressions.Regex

        Dim titles As IEnumerable(Of XElement) = xd.<musicLibrary>.<cd>.Where(Function(cd) IsMatch(cd.<artist>.Value, "^P.+ F.+")).<title>

これでartistが"Pink Floyd"のcd要素が選択され同じように、

結果は The title is 'The Dark Side of the Moon' と表示されます.

LINQMicrosoftが力を入れているだけあってなかなかおもしろく便利です.でもXPathは「標準」です.やっぱりMicrosoftにも2.0以降を実装してもらいたいという声はありますし私もそう思います.このあたりの事情はstackoverflow.comの以下のページに詳しく議論されています.

XPath and XSLT 2.0 for .NET? [closed]