ものがたり(旧)

atsushieno.hatenablog.com に続く

System.Xml.Query (その1)

skypeについて教えてもらったので、さっそく入れてみました。atsushienoを探してみて下さい。ただし僕はマイク持ってないんで、会話はできません(w

今日は、僕が今メインの仕事でやっているSystem.Xml 2.0のXQuery実装について、現状で知っていることを書いてみよう。といっても、僕は実はMS実装なんてほとんど参考にしていないけど。

XQueryの基本的な理解は、XSLT 1.0をXML Schemaで型付けして、関数型プログラミング言語のフレーバーを取り入れたものだと思えば、とりあえず大丈夫だ。「モジュール」と呼ばれる外部ファイルをインポートすることもできる。XSLT 2.0も、ほとんど同レベルの機能を有しているはずで、実際XSLTに慣れている人なら、こっちの方が楽だと思うけど、Microsoftではあえて*1XSLT 2.0は実装していない。そんなにXSLT 2.0 and XQuery 1.0 Serializationの実装は難しいのかしらん :-P

MS.NETでは、XQueryを実行するためのAPIは、System.Data.SqlXml.dllにあるSystem.Xml.Query.XQueryCommandクラスだけだ。しかし、XQueryを実装している低レベルAPIのほとんどは、なぜかSystem.Xml.dllの中にある、MS.Internal.Xml.* 以下で実装しているはずだ。

2003年のPDCで出てきたXQuery実装は、MappingSchemaと緊密に結びついていたり、Mark Fusselらの書いた"A First Look at ADO.NET and System.XML 2.0"とかいう本でも、Yukonでしか使えないようなサンプルばかりを書いていて(map:view()とか)、どうも純粋なW3C XQueryではないような雰囲気だった*2。んが、最新の.NET 2.0 betaでは、MappingはMS.Internalネームスペース内に隠遁し、XQueryCommandの引数でも使われなくなった。現在System.Data.SqlXml.dllに含まれているSystem.Xml.*以下のクラスがいくつあるか、知っている人はいるだろうか? 実はもうXQueryCommandしかない。そしてこのクラスは、System.Data.SqlXml.dllにある必要は全然ない。

XQueryCommandは、クエリ式を「コンパイル」して、(どうやら)内部でXQuery処理用のアセンブリを生成している(らしい)。これは新しいXsltCommandも同様だ。生成されるILのベースが同じなんだろう*3

なぜコンパイルモデルになったのかは一見明白ではない。が、Apache Xalanはだいぶ昔からスタイルシートJavaクラスにコンパイルするXSLTCという機能を実装していた。これがあまりにも速度が違ったので(5倍〜6倍くらい)、MSもまねしてみたのだろう。動的アセンブリの生成は、XmlSerializerでも用いられているテクニックだが、これが有効なのは、リフレクションが回避できるから、という理由による。一方XQueryでリフレクションが必要となる場面なんて、そんなに無いはずなのだが…?

Microsoftの実装は、どうやら関数の呼び出しなどが、CLRメソッドベースで作られているらしい。これは、MS.Internal.XmlにあるXQueryFuncLibraryやXsltLibraryといったクラスに、それらしい関数が多々あることからも推察できる。実はこのモデル、潜在的な機能拡張性が高いので、僕も同じように設計することにした。その原点はSAXONの設計にある。

このドキュメントを見てほしい。ここには、XQueryのモジュールインポートの構文で、Javaの任意のstaticメソッドを呼び出す例が書いてある。これを.NET的に置き換えると、こんな感じだ:

import module namespace math = "System.Math";

SAXONでは declare namespace math = "java:java.lang.math"; となっているけど、まあどちらでも良いだろう(ホントか)。

ちなみに.NET 1.1でも、XSLTではなくXPathに限られた話だが、カスタムXsltContextを渡すことで、任意のネームスペースに割り当てられた関数を、XsltContext.ResolveFunction()を通じて使うことができた。その内部で使用されているテクニックはリフレクションだ(のはず)。

標準関数でもこれをやってしまうと、どうしてもリフレクションしないと、パフォーマンスが悪くなってしまう。僕は今のところ標準関数をベタに書いたコードも提供する予定なので(monoのXmlSerializerもXalanも、両方のソリューションを提供している)、動的アセンブリ生成はそれほど必要性の高い仕事ではないけど、Microsoftはどうもこっちの道しか考えていないようなので、彼らには逃げ道が無いのだ。

XQueryの最適化*4は他にもいくつかテクがあるけど、とりあえず今日はここまで。

で、何で今日XQueryの話を書いているかというと、Monoでもnode constructorについてはようやくXQueryが実行できるようになったからなのだ。もしかしたら8月中に、いくつかのクエリが動作するようになるかもしれない。もう合計2ヶ月近くいじっているのだから、そろそろ出来てもおかしくはないだろう。

*1:ごく簡単に出来るのに、かつ需要の声が大きいのに、やらないのだから、「あえて」としか言いようが無い。

*2:最近でも、MSのXML開発者に「XQueryYukonでしか使えないようにするのはやめてほしい」と苦情が寄せられていた

*3:ちなみに、MicrosoftXML開発者は、XsltCommandが以前より4倍早くなったと宣伝しているけど、これはウソだ。僕がmonoのcorcompareで使っているxslで試してみたら、たかだか2倍くらいだった。しかも、これはコンパイルにかかる時間を除外しての話。コンパイルではIL生成とアセンブリロードがかかるので、格段に遅くなる。

*4:ええと、「実装の」最適化の方。クエリ式の最適化の話じゃありません。ごめんなさい