XSLT 2.0で便利になった機能(59) ロケーションステップでの式記述

今まで知らなかったといえばそれまでなのですが、掲題の機能は非常に便利です.一般的にソースドキュメントからなにがしかの値を求める場合、ロケーションステップを使用します.例えば /doc/chapter/sect/p など.でもこれを求めるときに入力ドキュメントにあるだけの状態ではなく、なんらかのフィルタをかけたい場合があります.例えばnormalize^space()で最後の要素の正規化した文字列値を取り出したいなど.このようなとき今まではつぎのようにやっていたと思います.


<xsl:variable name="stringValueOfP" as="xs:string*">
    <xsl:for-each select="/doc/chapter/sect/p">
        <xsl:sequence select="noralize-space(string(.))"/>
    </xsl:for-each>
</xsl:variable>

このコードの気持ちはわかるのですが、いまいち冗長です.XSLT 2.0(XPath 2.0)を使用すれば次のように簡単に書けてしまいます.

<xsl:variable name="stringValueOfP" as="xs:string*" select="/doc/chapter/sect/p/noralize-space(string(.))"/>

何がXPath 1.0と違うかというのとロケーションステップ(のたぶん最後)にこのように式がダイレクトにかけてしまうのです.上記の場合は式と言っても関数記述ですね.これはXPath 2.0の構文をよく見るとわかります.

まずパス式は次のように定義されています.


3.2 Path Expressions

[25]    PathExpr ::= ("/" RelativePathExpr?)
                   | ("//" RelativePathExpr)
                   | RelativePathExpr
[26]    RelativePathExpr ::= StepExpr *1 eq '') then 1 else 0))"/>

で文書中の空段落の数を求めることができます.

この手の問題があった場合、人はとにかくxsl:foreachを使ってしまい、結局迷路にはまってしまいますが、ロケーションステップに式を書くことで結構応用は広まりますね.

*1:"/" | "//") StepExpr)*


3.2.1 Steps

[27]    StepExpr ::= FilterExpr | AxisStep

AxisStepは、child::*とかparent::*とか、軸(Axis)を使ってロケーションステップの方向を定めます.ところでFilterExprは実は

3.3.2 Filter Expressions

[38]    FilterExpr  ::= PrimaryExpr PredicateList
[39]    PredicateList  ::= Predicate*

となっていて、PrimaryExpr、つまりリテラル、変数参照、カッコつきの式、.(コンテキストアイテム)、関数呼び出しが書けます.
上記の例はnoramilize-space()で関数呼び出しですね.

カッコつきの式を使えばもっと応用は広まり、例えば

<xsl:variable name="emptyPCount" as="xs:integer" select="sum(/doc/chapter/sect/p/(if (noralize-space(string(.