前回に引き続き,今回もはてなダイアリー形式からJUGEM形式に変換するXSLTスタイルシートを例に,XSLTの基本構文を学習します。なお,例1のXSLTスタイルシートは第4回と同じ内容です。


例1 はてなダイアリー形式からJUGEM形式に変換するXSLTスタイルシート

[画像のクリックで拡大表示]

◇xsl:if命令

 xsl:if命令(例1の38行目,41行目)は,test属性で指定された条件が真の場合にxsl:if命令の内容に記述されたテンプレートを実行します。test属性にはXPath式を記述し,真偽を判断します。

xsl:if命令による条件分岐

  <xsl:if test="name()='date'">
・・・
</xsl:if>

 test属性のXPath式に使用できる演算子は表1のとおりです。ただし,「<」や「<=」を使用する場合,「<」は『タグの開始』と見なされてしまうため,定義済み実体参照を使用して,「<」は「&lt;」,「<=」は「&lt;=」と記述します。「>」,「>=」は定義済み実体参照を使用しなくても記述できますが,「&gt;」,「&gt;=」と記述することで,見やすいXSLTスタイルシートを作成できます。

表1●XPath式で使用できる演算子
数値演算子 + 加算(足し算)
- 減算(引き算)
* 乗算(掛け算)
div 除算(割り算)
mod 除算した結果の余り
論理演算子 and 論理積(左辺が偽のときは右辺を評価しない)
or 論理和(左辺が真のときは右辺を評価しない)
比較演算子 = 等しい
!= 等しくない
< より小さい( &lt; と記述する)
> より大きい( &gt; と記述することもできる)
<= 以下( &lt;= と記述する)
>= 以上( &gt;= と記述することもできる)

 XSLTには一般的なプログラミング言語の「if ~ else ~」のelseに相当する命令がありません。しかし,XPathのnot関数を使うことで真偽の値を反転させることができます。例えば,「在庫数」要素の内容と「販売数」要素の内容を,xsl:if命令を使用して条件分岐する場合には,以下のような記述になります。

例:xsl:if命令のnot関数による真偽値の反転

  <xsl:if test="在庫数 &gt; 販売数">
商品の在庫があります。
</xsl:if>
<xsl:if test="not(在庫数 &gt; 販売数)">
商品の在庫がありません。発注を依頼してください。
</xsl:if>

◇xsl:choose命令

 XSLTで使用できる条件分岐の命令は,もう1つあります。それは,xsl:choose命令です。xsl:choose命令は,xsl:choose命令の内容として記述された一連のテンプレート(xsl:when要素またはxsl:otherwise要素)のうちのいずれか1つだけを実行します。xsl:choose命令を使用する際の制限としては,xsl:choose命令の内容に1つ以上のxsl:when要素が必要となります。xsl:otherwise要素は省略することもでき,記述する場合には1つだけとなります。xsl:when要素のtest属性は,前述のxsl:if命令と同じです。例1のXSLTスタイルシートの38行目~43行目をxsl:choose命令を使用して記述すると,以下のようになります。

xsl:choose命令を使用した例

  <xsl:choose>
<xsl:when test="name()='title'">
<xsl:value-of select="." />
</xsl:when>
<xsl:when test="name()='date'">
<xsl:value-of select="translate(.,'-','/')" />
</xsl:when>
</xsl:choose>

確認問題1
次の条件分岐命令のうち,正しく記述されているものを選択してください。
 
(a)
<xsl:if test="売上目標 < 売上">売上目標を達成しました。</xsl:if>
(b)
<xsl:if test="売上目標 > 売上">売上目標を達成できませんでした。</xsl:if>
<xsl:else>売上目標を達成しました。</xsl:else>
(c)
<xsl:choose>
  <xsl:when test="売上目標 &lt; 売上">売上目標を達成しました。</xsl:when>
<xsl:choose>
(d)
<xsl:choose>
  <xsl:otherwise test="売上目標 &lt; 売上">売上目標を達成しました。</xsl:otherwise>
</xsl:choose>

 正解は(c)です。(a)は,比較演算子に「<」を使用しています。「<」は『タグの開始』と見なされてしまいますので,定義済み実体参照「&lt;」を使用しなければなりません。(b)は,xsl:if命令は正しく記述されていますが,XSLTではxsl:else命令はありません。(c)は正しく記述されています。xsl:otherwise要素は省略することができます。xsl:choose命令の内容には,1つ以上のxsl:when要素が記述されていなければならず,xsl:otherwise要素のみを記述することはできません。このため(d)は誤りです。

 次にXSLTでノードをコピーする2つの命令について解説します。

◇xsl:copy-of命令

 xsl:copy-of命令は,ソースXML文書(元となるXML文書)の一部をそのままコピーしたい場合に使用します。select属性で指定したXPath式に該当するノードを属性や子孫ノードを含めて,すべてそのままコピーします。

  <xsl:template match="/">
<blog>
<entries>
<xsl:copy-of select="diary/day" />
</entries>
</blog>
</xsl:template>

 上記の例では,「day」要素,「date」属性,「title」属性,「body」要素がコピーされます(図1)。ただし,xsl:copy-of命令のselect属性にルートノード(/)を指定した場合は,ルートノードはコピーされず,その子孫ノードがコピーされます。

図1●変換後のXML文書の構造

 ルートノード(/)はルート要素ではありません。XPathでは,ルート要素はルートノードの子ノードとなりますので,注意が必要です(図2)。

図2●XPathでのルートノードとルート要素の違い



xsl:copy-of命令を使用した変換結果

[画像のクリックで拡大表示]

◇xsl:copy命令

 xsl:copy命令は,xsl:copy-of命令とは異なり,カレントノードが要素の場合は,要素だけをコピーし,その要素が持っている属性や子孫ノードをコピーしません。xsl:copy命令は,カレントの要素だけをコピーして新しい属性を追加したり,要素の内容を変更したりする場合に使用します。

  <xsl:template match="/">
<blog>
<entries>
<xsl:apply-templates select="diary/day" />
</entries>
</blog>
</xsl:template>

<xsl:template match="day">
<xsl:copy>
<date><xsl:value-of select="@date" /></date>
<title><xsl:value-of select="@title" /></title>
<description><xsl:value-of select="body" /></description>
</xsl:copy>
</xsl:template>



xsl:copy-of命令を使用した変換結果

[画像のクリックで拡大表示]

確認問題2
次のXML文書にXSLTスタイルシートを適用した場合,正しい変換結果を選択してください。
XML文書
<?xml version="1.0" encoding="Shift_JIS" ?>
<diary>
  <day date="2006-08-30"  title="ようやくXML Schemaの勉強開始" >
    <body>今日XML Schemaの勉強を開始しました。結構難しい・・・</body>
  </day>
</diary>
XSLTスタイルシート
<?xml version="1.0" encoding="Shift_JIS"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <data>
      <xsl:copy-of select="." />
    </data>
  </xsl:template>
</xsl:stylesheet>

(a)
<?xml version="1.0" encoding="UTF-8"?>
   <data>
     <diary>
       <day date="2006-08-30" title="ようやくXML Schemaの勉強開始">
         <body>今日XML Schemaの勉強を開始しました。結構難しい・・・</body>
       </day>
     </diary>
   </data>
(b)
<?xml version="1.0" encoding="UTF-8"?>
   <data>
     <day date="2006-08-30" title="ようやくXML Schemaの勉強開始">
       <body>今日XML Schemaの勉強を開始しました。結構難しい・・・</body>
     </day>
   </data>
(c)
<?xml version="1.0" encoding="UTF-8"?>
   <data>
     <diary/>
   </data>
(d) xsl:copy-of命令のselect属性の記述に誤りがあるので,正しい変換結果はない。

 この問題のXSLTスタイルシートでは,ルートノードを処理するテンプレートルールの中で,xsl:copy-of命令が記述されており,そのselect属性にはカレントノード「.」が指定されています。すなわち,このXSLTスタイルシートはルートノードを含め,属性やその子孫要素,すべてコピーするように記述されています。よって正解は(a)です。




野中 康弘
インフォテリア 教育部エンジニア。十数年間システム開発に携わり,そのうちの半分をデータベース管理者として奮闘する。現在は,インフォテリア認定教育センターのサポートや, XML技術者認定制度の「XMLマスター」に対応したコーステキストの開発などを担当している。