XSLT2.0の感覚

Written byがいすと

7.2 XPath内に条件分岐を書く

レポートなどで参考文献リストを作るときを考えます。参考文献リストとは、どのような文献を引用したかを一覧にしたものです。ここでも紹介していた蛾類通信の記事を例にすればこんな感じになります:

岸田泰則, 2002. 対馬で発見された日本未記録のカレハガ. 蛾類通信 219: 359 - 360.

最初から順に、著者、発行年、タイトル、雑誌名、号数、ページ数となっています。参考文献リストのフォーマットは、大体は同じですが少しずつ違う場合があります。ですから、引用方法に左右されない参考文献XML文書を作成しておき、異なるXSLTシートで参考文献リストの出力を操作する、と言った作業で文献リストの作成を効率化できます。このように、参考文献リストはXSLTを使用する格好の材料ともいえるでしょう。

さて、ここでは、ページ数の出力に注目します。上の例では、「359ページから360ページ」を引用していることが「359 - 360」で示されています。359ページだけなら「359」ですね。さらに、同じ本や雑誌のいくつかの場所を引用する場合があります。この時は、「359 - 360, 365, 399 - 401.」などとカンマで区切ります。そして、最後はピリオドを出力します。

これらのページ出力をXSLTでサポートするため、以下のようなページ数を表すXMLフォーマットを考えることにします。

<bib>
  <pages start="359" end="360"/>
  <pages start="365"/>
  <pages start="399" end="401"/>
</bib>

bibタグは一つの参考文献のデータを表します(本来ならもっと色々なタグが必要です)。各引用部分はpagesタグで示されます。開始ページと終了ページは、それぞれstart属性とend属性で指定されます。1ページだけの場合にはend属性を指定しません。

では、上のページ数を表すXMLを「359 - 360, 365, 399 - 401.」に変換する処理を考えます。まずはXSLT1.0から。

<xsl:template match="bib">
  <p>
    <xsl:apply-templates select="pages"/>
  </p>
</xsl:template>

<xsl:template match="pages">
  <xsl:value-of select="@start"/>
  <xsl:if test="@end">
    <xsl:value-of select="concat(' - ', @end)"/>
  </xsl:if>
  <xsl:choose>
    <xsl:when test="position()!=last()">
      <xsl:text>, </xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>.</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

【出力】
<p>359 - 360, 365, 399 - 401.</p>

この中で、end属性があれば" - end"concat()を使って出力しているのと、最後の要素以外はカンマ、最後はピリオドを出力している部分に注目してください。今度はXSLT2.0で書いてみます。呼び出し部は変わらないので、pagesにマッチするテンプレートだけを示します。

<xsl:template match="pages" mode="xslt2">
  <xsl:value-of select="@start"/>
  <xsl:value-of select="if (@end) then concat(' - ', @end) else ''"/>
  <xsl:value-of select="if (position()!=last()) then ', ' else '.'"/>
</xsl:template>

これまたずいぶん短くなりました。キモになるのがXPathに書かれたif (条件) then (文字列1) else (文字列2)と言う部分です。これは、「カッコ内に書かれた条件が真の時に文字列1を、偽の時に文字列2を出力せよ」という処理を示します。(条件)はXPathで書けますから、xsl:choosexsl:ifとほぼ同じ機能を備えているといえます。簡単な出力の制御のためにxsl:choosexsl:ifを使用している場合、このステートメントを使用すると非常にすっきりと書くことができます。なお、このif文はいくらでもネストできますので、かなり複雑な条件分岐をも書くことができます。

前に紹介したseparatorを使用してカンマを出力させる方法も考えられますが、最後のピリオド処理を別に書く必要があり、同じ末尾の文字であるカンマとピリオドの出力ルーチンが別々になってしまいます。その点、if thenを使うと、「条件によって末尾の文字を変えている」という意図がより読みとりやすくなると考えます。

if then構文ではelse以下を省略することはできないようです。ですから、xsl:ifをこれで置き換えるためには、最後に else ''という偽の場合のダミールーチンを入れる必要があります。