閑話休題: document()関数

document()関数はXSLT2.0ではdoc()関数にとって代わられ、過去との互換性を維持するためだけにあるようです.XSLT1.0でもdoument()関数はよく使ったのですが、いまいちその性格が飲み込めていませんでした.失敗もありました.
 
まず私のdocument()関数の使い方は、もっぱら
 
<xsl:variable name="subdoc" select="..."/>
<xsl:apply-templates select="documet($subdoc)"/>
 
というような使い方でした.つまり、入力XMLファイルから直接アクセスできるXMLが、href="input_2.xml"などというように単純に記述されているのではなく、その@hrefで示されているファイル名をいろいろ加工して、<xsl:variable>に格納し、それをdocument()関数のパラメータに指定してアクセスしなければならなかったのです.
 
このため、ファイル名だけを上記の$subdocに格納すると、「スタイルシートファイル相対」でアクセスしてしまうという問題がありました.仕方なく、<xsl:param>で、入力XMLファイルのフォルダ名を受け取って、絶対パスに加工してアクセスしていました.
 
ところがよくXSLT1.0仕様を読むと、document()関数には2番目のパラメータがあります.データタイプはnode-setで、そのベースURIが相対URIを解決するために用いられるとあります.(ノードのベースURIとは一般にはその入力となったXMLファイルのURIです.)
 
<xsl:apply-templates select="documet($subdoc,/)"/>
 
とやれば、入力XMLファイル相対でアクセスしてくれます.
 
<xsl:apply-templates select="documet($subdoc,document(''))"/>
 
とやれば、スタイルシートファイル相対でアクセスしてくれます.(document('')はスタイルシートのドキュメントノードを返してくれるためです.)
 
当時私はこの2番目のパラメータの存在を知りませんでした.2番目のパラメータに/を指定すれば良いだけなのに、スタイルシート設計で余分なパラメータを導入してかえって複雑なことをしていました.やはり仕様はよく読まないといけないです.
 
さて、上記の場合、第1パラメータは<xsl:variable>でstringに評価されるものでした、ところがXSLT1.0の仕様を読むと、このパラメータはobjectで、node-setか、そうでないもの(stringに変換される)となっています.<xsl:variable>は後者に該当します.それではnode-setの場合とはどんなものなのでしょうか?
 
一番簡単な例が入力XMLファイルに、
 
<subdoc href="input_2.xml"/>
 
などとあって、document(@href)でアクセスする場合です.これはinput_2.xmlが処理されるだけです.@hrefはnode-setといってもnodeが一個だけのものです.
 
もうちょっと複雑な例として、
<data>
    <subdoc href="input_2.xml"/>
    <subdoc href="input_3.xml"/>
    <subdoc href="input_4.xml"/>
</data>
 
とあり、以下のようなテンプレートがあるとします.
 
<xsl:template match="data">
    <xsl:copy>
        <xsl:apply-template select="document(subdoc/@href,/)"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="*">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
    </xsl:copy>
</xsl:template>
 
こうすると出力XMLには、<data>要素の下に、入力XMLファイルと同じ位置にあるinput_2.xml~input_4.xmlがすべてコピーされます.subdoc/@hrefはnode-setで、その一つ一つのnodeをstringに評価して順に処理してくれるのです.
 
このような機能を一般化しようとしたのが、XSLT2.0のcollection()関数なのかもしれません.XSLT2.0では、doument()関数の第1パラメータはより一般化されitem()*になっています.