閑話休題:titleのテンプレート

DocBookにもDITAにも必ずtitleがあります.タイトルは目次ページや、参照(xref)などでも使われます.タイトルそのものを本文中で処理するときは、例えばDocBook→XSL-FOなら、
 
<xsl:template match="title">
    <fo:block use-attribute-sets="xxxTitle">
        <xsl:apply-templates/>
    <fo:block>
</xsl:template>
 
のようなもので良いと思いますが、目次ページや参照では変数にタイトルの内容を格納したい場合が出てきます.このとき、titleの文字内容だけで良いので、XSLT1.0では次のようなコードを書いていました.
 
<xsl:variable name="title">
    <xsl:apply-templates select="title" mode="TEXT_ONLY"/>
<xsl:variable>
<!-- たいていの要素はこれでOK -->
<xsl:template match="*" mode="TEXT_ONLY">
    <xsl:apply-templates mode="TEXT_ONLY"/>
</xsl:template>
 
では、XSLT2.0では、$titleにどのようなas属性をつけるのがよいでしょうか?
as="xs:string"なら
 
<title>Introduction</title>
 
の場合はエラーが出ません.でも
 
<title><b>XSLT</b> Programmer's <i>reference</i><title>
 
のような場合は、
 
XTTE0570: A sequence of more than one item is not allowed as the value of variable $title
 
のエラーになるはずです.何故かと言うと、この場合、<b>や<i>などのインライン要素があるため、複数のテキストノードが<xsl:apply-templates mode="TEXT_ONLY"/>の結果として生成されてしまうためです.つまり、XSLTプロセッサはtext()はxs:stringにキャストしてくれても、text()*はxs:stringにはキャストしてくれないのです.正解は以下のようだと思います.
 
<xsl:variable name="title" as="xs:string">
    <xsl:variable name="tempTitle" as="text()*">
        <xsl:apply-templates select="title" mode="TEXT_ONLY"/>
    </xsl:variable>
    <xsl:sequence select="string-join($tempTitle,'')"/>
<xsl:variable>
 
これは前回の「XSLT2.0で便利になった機能(7) 」を書いていたときも使っていました.このとき、$tempTitleはas="xs:string*"でしたが、text()*からxs:string*へは問題なくキャストしてくれます.string-joinは複数のsequenceのxs:stringを結合してくれます.
どうでしょう.いやXSLT2.0はやっかいだと思われるかもしれません.しかし私はXSLT1.0では如何にXSLTプロセッサの寛大な振る舞いに依存していたのんきなコードを書いていたものだと反省させられます.XSLT2.0で書くようになれば他のプログラミング言語同様、データの型に敏感になれると思います.