XSLT3.0への道(32) 使ってみた感想

XSLT 3.0はやはり実際の開発現場で使ってみるべきだと思います.そうしないとなかなか感触というものがわからない.(もちろん顧客の条件でおいそれとXSLTのバージョンを上げるわけにはゆかない場合もあるでしょうが)

ちなみにXSLT勧告上の従来の2.0との違いを記述したページは以下にあります.


たまたま私がやっている分野では特にXSLTのバージョンにこだわる必然性もなかったので、<xsl:stylesheet version="3.0">と思い切って書いて実際に使った見ました.以下ピンからキリまでありますが使ってみた感想です.


① xsl:assertは便利です.

さまざまな言語のアザーションに該当するxsl:assertが導入されました.例えば以下の様に使えます.

<xsl:assert test="exits($prmListLevel)" select="'[ASSERT: topic/p] $prmListLevel is empty!'"/>

@testで指定された条件がtrue()になったとき、xsl:messageと同じように@selectで指定した文字列が表示されます.同じことをXSLT 2.0で書くと

<xsl:if test="not(exists($prmListLevel))">
    <xsl:message select="'[ASSERT: topic/p] $prmListLevel is empty!'"/>
</xsl:if>

と書かねばなりませんからxsl:assertの方が簡単です.かつSaxonではコマンドラインの設定でassertionをON/OFFすることができます.

② xsl:try~xsl:catchで例外処理ができます.

特にキャストを行う場合、エラーはつきものです.もちろん"castable as"を使って事前にチェックすればよいのですが、それも手間なときは例えばつぎのように書くことができます.文字列から日付型への変換です.

    <xsl:function name="tmf:toDateTime" as="xs:dateTime?">
        <xsl:param name="prmDateTime" as="xs:string"/>
        <xsl:try>
            <xsl:choose>
                <xsl:when test="contains($prmDateTime, 'T')">
                    <xsl:sequence select="xs:dateTime($prmDateTime)"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:sequence select="dateTime(xs:date($prmDateTime), xs:time('00:00:00'))"/>
                </xsl:otherwise>
            </xsl:choose>
            <xsl:catch errors="*">
                <xsl:sequence select="()"/>
            </xsl:catch>
        </xsl:try>
    </xsl:function>

③ xsl:evaluateで動的にXpathを評価できます.

これは使ってみると衝撃的です.自分で文字列をビルドしてそれをXPathとして結果を受け取ることができます.

次の例は、この前のxsl:tryと合わせて任意の文字列をXPathとして評価します.xsl:tryを使うのはXPathの評価にはエラーはつきものだからです.

<xsl:function name="tmf:evaluateStyleXPath" as="xs:string">
<xsl:param name="prmXPath" as="xs:string"/>
<xsl:try>
<xsl:variable name="xPathResult" as="xs:string">
<xsl:evaluate xpath="$prmXPath"/>
</xsl:variable>
<xsl:sequence select="$xPathResult"/>
<xsl:catch errors="*">
<!--エラー処理-->
</xsl:catch>
</xsl:try>
</xsl:function>

④ 便利なXPath 3.1の関数が利用できます.

こんな例がありました.DITAのインスタンスをそれなりに大きな規模でオーサリングして途中まで来たけれど、はたと間違いに気が付いた.すべてのインスタンスで一括して文字列置換を行いたい.(それも簡単な文字列置換でなく、正規表現をつかうようなややこしいやつです.)

(1) すべてのDITAインスタンスを処理する.

今まではantの機能を使っていましたが、あるディレクトリの下の全ファイルを処理するということはいとも簡単にXSLT 3.0ではできます.XPath 3.1のuri-collection()関数を使ってその配下のファイルのURIををxs:anyURI+で受け取ることができます.これを順に処理すればよい訳です.

<xsl:variable name="imageUriCollection" as="xs:anyURI+" select="uri-collection($pTopicDirUrl)"/>

(2) 文書型宣言がついてしまったファイルでもunparsed-text-lines()で読み込んで処理できます.

一般にはXSLTは入力はXMLファイルで、文書型宣言があると、パーサーを通じてDTDで定義した初期値がついてとろくなことがありません.ところがunparsed-text-lines()を使えばテキストファイルの1行単位に処理が出来てしまうので、このようにDITAのインスタンスをなんとか一括で文字列変換したいときにはうってつけです.

    <xsl:for-each select="unparsed-text-lines($topicFileUrl,'UTF-8')">
        <xsl:variable name="line" as="xs:string" select="."/>
        <!--行単位の処理を行い出力に書き出す.-->
    </xsl:for-each>

これは対象が行分割されていな場合には超強力です.でも行分割されてしまっている場合はさすがに事前にインスタンスの手直しが必要です.

⑤ mapが使えます.

マップは、キーと値の組み合わせで構成されます.文書全体にわたるデータからマップを作って、それを各所で使用する場合が例えばDITA⇒.docxの変換ではそこらで必要になりますが、これさえあれば簡単に対応できます.(DITA⇒XSL-FOの変換では出力が単独ファイルなので不要なのですが、.docxではファイルが箇所にばらけていて、かつ一貫した関連性を持たせなければならないためmapは有効になります)

というっようなわけで、実際にXSLT 3.0を使ってみた感想は、難しいというよりはプログラマーが必要なものが豊富にそろっていて、ある意味やりたいことがどんどんできる.という感じです.まだまだ勧告されて日は浅いですが、実際に使ってみるのが一番と感じました.