第 11 回 XSLT

本日の内容


このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。

11-1. XSLT

XSLT(XSL Transformation) は XPath でマッチした部分に対して変換規則を与 えることにより、 XML 文書を別の形式に変換するものです。 出力方法は XML の他、 HTML と TEXT があります。

XSLT のファイルはスタイルシートと呼ばれます。 しかし、XML から XML への関数を定義でき、さらに様々な複雑な構文を持つという意味で、一種のプログラミング言語になっています。 条件分岐やループ構造なども含んでいます。

単なる表示

まず、始めに単純な変換の例を示します。

例11-1

以下の XML 文書があったとします。


<?xml version="1.0" encoding="Shift_JIS" ?>
<dummy>
Hello World
</dummy>
テキストファイル

このような XML 文書に対して、次のような XSLT スタイルシートを与えます。


<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "text"
     media-type ="text/plain"
     indent = "no"
     />
  <xsl:template match="/"> 
    <xsl:apply-templates select="dummy"/>
  </xsl:template> 
  <xsl:template match="dummy">
dummy 要素の内容は<xsl:value-of select="." />です。
</xsl:template>
</xsl:stylesheet>

すると次のような出力が得られます。


dummy 要素の内容は
Hello World
です。

例11-2

HTML 文書

前述の XML 文書に対して、以下のスタイルシートを与えます。


<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "html"
     doctype-public = "-//W3C//DTD HTML 4.01//EN"
     doctype-system = "http://www.w3.org/TR/html4/strict.dtd" />
  <xsl:template match="/"> 
    <html lang="ja">
      <head>
	<title>Test</title>
      </head>
      <body>
	<xsl:apply-templates select="dummy"/>
      </body>
    </html>
  </xsl:template> 
  <xsl:template match="dummy">
    <p>
      <xsl:value-of select="." />
    </p>
  </xsl:template>
</xsl:stylesheet>

すると以下の出力が得られます。


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
<head>
<META http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<title>Test</title>
</head>
<body>
<p>
Hello World
</p>
</body>
</html>

このように XML 文書をテキストファイルや HTML 文書に変換できます。

Java での用例

Java では既に前に取り上げた XML 文書の出力用に使用していた javax.xml.transform.TransformerFactory クラスを使用します。 まず、スタイルシートを StreamSource オブジェクトに変換します。 一方、TransformerFactory オブジェクトを作成します。 そして、 TransformerFactory オブジェクトの newTransformer メソッドで Transformer を作成する際に、用意しておいたスタイルシートを 変換した StreamSource オブジェクトを与えます。 すると、与えたスタイルシートを用いる Transformer オブジェクトが作られます。

例11-3


StreamSource ss = new StreamSource(new FileInputStream(XSLT ファイル名));
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(ss);

作った Transformer オブジェクトに XML 文書を StreamSource としたものと、 出力先を StreamResult としたもの与えた transform メソッドを起動すると、 変換が行われます。

例11-4


StreamSource xmlSource = new StreamSource(new FileInputStream(XML文書ファイル名));
StreamResult out = new StreamResult(System.out));

transform(xmlSource,out);

ブラウザによる表示

Firefox や Microsoft の Internet Explorer(IE) などのブラウザは XSLT ス タイルシートを処理できます。  ブラウザに処理させるには、 XML 文書内に次の PI を与えます。


<?xml-stylesheet href="スタイルシートの URL" type="text/xsl" ?>

同一フォルダに XML 文書と XSLT スタイルシートのファイルが入っていて、 ファイルとして XML 文書として開く場合は、URL は単なるファイル名で構いません。 このように変えた XML 文書をブラウザからファイルとして開くと処理されます。 上記の例などで試してください。

11-2. XSLTの基本

ここではまず XSLT の単純な用法について説明します。

基本構文

XSLT は次の構文になります。


<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
...
</xsl:stylesheet>

上記の ... に XSLT の要素を記述します。 なお、 基本的に XSLT の要素は全て xsl: という接頭語を付けて記述します。

出力の指定

xsl:output 要素は出力の形式を指定します。 これは、 javax.xml.transformer.Transformer オブジェクトの setOutputProperties で設定する項目と同じです。 但し、両方指定されたらプログラムの方が優先されます。

属性の一部を下記に示します。

属性名許される値意味
method text, html, xml のいずれか 出力する形式を表します。指定しないと xml になります。
encoding UTF-8, Shift_JIS などの漢字コード IANA に登録されている文字コード名
indent yes または no 出力時に改行などを入れて整形するか否か
media-type MIME のメディアタイプ 省略するとデフォルト値 text/html, text/plain, text/xml になります。
version 数値 省略すると XML の時は 1.0, HTML の時は 4.0 (4.01 ではない)になりま す。
omit-xml-declaration yes または no yes だと XML 宣言を出力しません。デフォルト値は no です。
standalone yes または no XML 宣言の standalone 値を設定します。
doctype-system URL DTD の URL
doctype-public 公開識別子 DTD の公開識別子

典型的な xsl:output の例は次のようになります(文字コードは指定してませ ん)。

テキストファイル

  <xsl:output
     method = "text"
     media-type ="text/plain"
     indent = "no"
  />
XHTML 1.1

  <xsl:output
     method = "xml"
     indent = "yes"
     doctype-public = "-//W3C//DTD XHTML 1.1//EN"
     doctype-system = "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
  />
HTML 4.01

  <xsl:output
     method = "html"
     indent = "yes"
     doctype-public = "-//W3C//DTD HTML 4.01//EN"
     doctype-system = "http://www.w3.org/TR/html4/strict.dtd"
  />

テンプレート

XSLT のもっとも重要な要素として、 xsl:template と xsl:apply-templates があります。

XSLT は XML 文書のノードを処理しますが、この時、 xsl:template の match 属性に書かれた XPath 式と照合し、照合した template の内容を出力に展開 します。 つまり、次の XSLT スタイルシートはどのような XML 文書に対しても Hello World を出力します。


<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "text"
     media-type ="text/plain"
     indent = "no"
  />
  <xsl:template match="/"> 
      Hello World
  </xsl:template> 
</xsl:stylesheet>

xsl:template の中に xsl の要素でないものを書くとそのまま出力されます。 また、その中に xsl:apply-templates を書くと、 他の xsl:template を検索し、ふさわしいものを見つけたら、 xsl:apply-templates を書いた場所に展開します。

xsl:apply-templates には select に XPath 式を書きます。 xsl:template の内部では match した XML 文書のノードの子ノードが有効に なりますので、その子ノードに対する相対 XPath 式を select に書きます。 また、 apply-templates の select に何も指定しないと、対象となる全ての 要素が処理されます。

値の出力

xsl:template 内で xsl:value-of 要素は select 属性に書いた XPath 式の値 を、そのまま出力します。

例11-5

以下の XML 文書を HTML に変換することを考えます。

<?xml version="1.0" encoding="Shift_JIS" standalone="no"?>
<itemlist>
  <item>
    <data name="品名" value="りんご"/>
    <data name="単価" value="200"/>
    <data name="個数" value="3"/>
    <data name="合計" value="600"/>
  </item>
  <item>
    <data name="品名" value="みかん"/>
    <data name="単価" value="100"/>
    <data name="個数" value="5"/>
    <data name="合計" value="500"/>
  </item>
  <item>
    <data name="品名" value="もも"/>
    <data name="単価" value="300"/>
    <data name="個数" value="1"/>
    <data name="合計" value="301"/>
  </item>
</itemlist>

これに対して次のように処理をするスタイルシートを与えます。

  1. 各 item 要素ごとに dl 要素を作る
  2. 各 data 要素に対して、 name 属性の値を dt 要素の値にし、 value 属 性の値を dd 要素の値にする。

<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "html"
     indent = "yes"
     doctype-public = "-//W3C//DTD HTML 4.01//EN"
     doctype-system = "http://www.w3.org/TR/html4/strict.dtd" />
  <xsl:template match="/">
    <html lang="ja">
      <head>
	<title>Test</title>
      </head>
      <body>
	<xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>
  <xsl:template match="item">
    <dl>
      <xsl:apply-templates/>
    </dl>
  </xsl:template>
  <xsl:template match="data">
    <dt><xsl:value-of select="@name"/></dt>
    <dd><xsl:value-of select="@value"/></dd>
  </xsl:template>
</xsl:stylesheet>

上記のスタイルシートを与えると、以下の HTML が得られます。


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
<head>
<META http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<title>Test</title>
</head>
<body>
<dl>
<dt>品名</dt>
<dd>りんご</dd>
<dt>単価</dt>
<dd>200</dd>
<dt>個数</dt>
<dd>3</dd>
<dt>合計</dt>
<dd>600</dd>
</dl>
<dl>
<dt>品名</dt>
<dd>みかん</dd>
<dt>単価</dt>
<dd>100</dd>
<dt>個数</dt>
<dd>5</dd>
<dt>合計</dt>
<dd>500</dd>
</dl>
<dl>
<dt>品名</dt>
<dd>もも</dd>
<dt>単価</dt>
<dd>300</dd>
<dt>個数</dt>
<dd>1</dd>
<dt>合計</dt>
<dd>301</dd>
</dl>
</body>
</html>
表示例
品名
りんご
単価
200
個数
3
合計
600
品名
みかん
単価
100
個数
5
合計
500
品名
もも
単価
300
個数
1
合計
301

一方、要素の直書きの他に、 xsl:element, xsl:attribute, xsl:text, xsl:processing-instruction, xsl:comment があります。 また、 xsl:attibute の集まりを管理する xsl:attribute-set があります。 これは、 name 属性で名前を付けておき、使用するには xsl でない他の要素 中に xsl:use-attribute-set 属性で名前を指定します。

一方、 xsl:copy は特定のノードセットをそのまま出力としてコピーします。

template の mode

xsl:template と xsl:apply-templates には mode 属性があります。 mode 属性が指定されていると、その mode の種類だけ同じノードへ解釈が行われます。 また、その時、同じ mode 同士の xsl:template と xsl:apply-templates が結びつけ られます。

例11-6

上記のデータについて、下記の XSLT スタイルシートを与えると、 table の 表示に変換した HTML が得られます。


<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "html"
     indent ="yes"
     doctype-public = "-//W3C//DTD HTML 4.01//EN"
     doctype-system = "http://www.w3.org/TR/html4/strict.dtd"
/>
  <xsl:template match="/"> 
    <html lang="ja">
      <head>
	<title>Test1</title>
      </head>
      <body>
	<table border="1">
	  <thead>
	    <tr>
	      <xsl:apply-templates select="//item[1]/data" mode="head"/>
	    </tr>
	  </thead>
	  <tbody>	
	    <xsl:apply-templates select="//item" mode="body"/>
	  </tbody>
	</table>
      </body>
    </html>
  </xsl:template> 
  <xsl:template match="data" mode="head" >
  <th><xsl:value-of select="@name"/></th>
  </xsl:template>
  <xsl:template match="item" mode="body">
    <tr>
	<xsl:apply-templates select="data" />
    </tr>
  </xsl:template>
  <xsl:template match="data">
    <td>
      <xsl:value-of select="@value"/>
    </td>
  </xsl:template> 
</xsl:stylesheet>

このように head モードと body モードで別々に処理をさせます。 mode の指定の無い data とマッチする template もありますが、これは head のモードからは select されません。


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
<head>
<META http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<title>Test1</title>
</head>
<body>
<table border="1">
<thead>
<tr>
<th>品名</th><th>単価</th><th>個数</th><th>合計</th>
</tr>
</thead>
<tbody>
<tr>
<td>りんご</td><td>200</td><td>3</td><td>600</td>
</tr>
<tr>
<td>みかん</td><td>100</td><td>5</td><td>500</td>
</tr>
<tr>
<td>もも</td><td>300</td><td>1</td><td>301</td>
</tr>
</tbody>
</table>
</body>
</html>
変換結果
品名単価個数合計
りんご2003600
みかん1005500
もも3001301

あらかじめ用意されているテンプレート

なにも指定していないときも、以下のテンプレートは定義されています。

  1. 
    <xsl:template match="*|/">
      <xsl:apply-templates/>
    </xsl:template>
    

    これはルート要素、その他、任意の要素にマッチします。 そして、マッチしたら、その子要素に対して、 apply-templates を処理させ ます。

  2. 
    <xsl:template match="*|/" mode="m">
      <xsl:apply-templates mode="m"/>
    </xsl:template>
    

    上記のルールは任意のモードでも有効です。

  3. 
    <xsl:template match="text()|@*">
      <xsl:value-of select="."/>
    </xsl:template>
    

    テキストや属性にマッチして、値を全て表示します。 このルールがあるため、単に <xsl:apply-templates /> と書くだけで 内容が全て表示されます。

  4. 
    <xsl:template match="processing-instruction()|comment()"/>
    

    一方、 PI やコメントに対しては展開しないのがデフォルトの動作です。

以上のテンプレートは優先度がもっとも低いので、再定義することで定義が上 書きされます。

11-3. プログラミング言語としてのXSLT

XSLT は単なる変換だけではありません。 条件分岐、繰り返しなど様々な制御構造があります。

xsl:if

test 属性に論理値を計算する XPath を記述することで、真であるときだけテ キストを展開することができます。

<xsl:if test="position()!=last()">,</xsl:if>

このようにすると要素を区切るカンマを最後に付加せずに済みます。

例11-7

一次方程式を解くスタイルシートを示します。 始めに、係数の入った XML 文書を示します。


<?xml version="1.0" encoding="Shift_JIS" standalone="no"?>
<houteishikilist>
<houteishiki>
<keisuu>2</keisuu><keisuu>3</keisuu>
</houteishiki>
<houteishiki>
<keisuu>1</keisuu><keisuu>-5</keisuu>
</houteishiki>
</houteishikilist>

これに対して、とりあえず、式と解の表示を行うスタイルシートを示します。


<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "html"
     indent = "yes"
     doctype-public = "-//W3C//DTD HTML 4.01//EN"
     doctype-system = "http://www.w3.org/TR/html4/strict.dtd" />
  <xsl:template match="/"> 
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
      <head>
	<title>一次方程式</title>
      </head>
      <body>
	<ol>
	<xsl:apply-templates select="//houteishiki"/>
	</ol>
      </body>
    </html>
  </xsl:template> 
  <xsl:template match="houteishiki">
    <li>
      <p>
	<xsl:value-of select="keisuu[1]/text()"/>
	  x+
	<xsl:value-of select="keisuu[2]/text()"/>
	  =0 の解は、 x=
	<xsl:value-of select="- keisuu[2]/text() div keisuu[1]/text()"/>
	です。
      </p>
    </li>
  </xsl:template>
</xsl:stylesheet>

ここで、定数項がマイナスの時は '+' を表示しないようにし、さらに x の係 数が 1 の時も表示しないようにしたのが次のスタイルシートです。


<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "html"
     indent = "yes"
     doctype-public = "-//W3C//DTD HTML 4.01//EN"
     doctype-system = "http://www.w3.org/TR/html4/strict.dtd" />
  <xsl:template match="/"> 
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
      <head>
	<title>一次方程式</title>
      </head>
      <body>
	<ol>
	<xsl:apply-templates select="//houteishiki"/>
	</ol>
      </body>
    </html>
  </xsl:template> 
  <xsl:template match="houteishiki">
    <li>
      <p>
	<xsl:if test="keisuu[1]/text() != 1">
	  <xsl:value-of select="keisuu[1]/text()"/>
	</xsl:if>
	<var>x</var>
	<xsl:if test="keisuu[2]/text() &gt;= 0">
	  +
	</xsl:if>
	<xsl:value-of select="keisuu[2]/text()"/>
	  =0 の解は、 <var>x</var> =
	<xsl:value-of select="- keisuu[2]/text() div keisuu[1]/text()"/>
	です。
      </p>
    </li>
  </xsl:template>
</xsl:stylesheet>

xsl:choose, xsl:when, xsl:otherwise

xsl:if では、 else が使用できません。 複数の条件のうち一つを選択したり、条件に合わない場合の処理を指定したい 場合は xsl:choose を使用します。

例11-8

以下のようにすると、係数が 0 のような方程式も処理できます。


<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "html"
     indent = "yes"
     doctype-public = "-//W3C//DTD HTML 4.01//EN"
     doctype-system = "http://www.w3.org/TR/html4/strict.dtd" />
  <xsl:template match="/"> 
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
      <head>
	<title>方程式</title>
      </head>
      <body>
	<ol>
	<xsl:apply-templates select="//houteishiki"/>
	</ol>
      </body>
    </html>
  </xsl:template> 
  <xsl:template match="houteishiki">
    <li>
      <p>
	<xsl:if test="keisuu[1]/text() != 1">
	  <xsl:value-of select="keisuu[1]/text()"/>
	</xsl:if>
	<var>x</var>
	<xsl:if test="keisuu[2]/text() &gt;= 0">
	  +
	</xsl:if>
	<xsl:value-of select="keisuu[2]/text()"/>
	  =0 の解は、
	<xsl:choose>
	  <xsl:when test="keisuu[1]=0">
	    <xsl:choose>
	      <xsl:when test="keisuu[2]=0">
		任意の数です。
	      </xsl:when>
	      <xsl:otherwise>
		ありません。
	      </xsl:otherwise>
	    </xsl:choose>
	  </xsl:when>
	  <xsl:otherwise>
	    <var>x</var> =
	    <xsl:value-of select="- keisuu[2]/text() div keisuu[1]/text()"/>
	    です。
	  </xsl:otherwise>
	</xsl:choose>
      </p>
    </li>
  </xsl:template>
</xsl:stylesheet>

xsl:for-each

select 属性で指定した XPath 式によるノードセットに対して、それぞれの ノードに対してコンテンツ内が処理されます。

例11-9

下記は、上記の itemlist を CSV(Comma Separated Value) に変換するスタイ ルシートです。

ここで、xsl:text はテキストノードを生成する要素です。 通常は、文字列や xsl 名前空間以外の要素書けばそのままノードになります。 しかし、ここで使用しているのは、テキストにおいて改行をコントロールする ためです。 実体参照 &#13; (CR)は復帰、&#10; は改行(LF)を意味しています。 CSV では改行記号が CRLF なので、明示的に指定するため、このようなコント ロールコードのエスケープを無効にする指定を行っています。


<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "text"
     media-type ="text/comma-separated-values"
     indent = "no"
     />
  <xsl:template match="/itemdata"> 
    <xsl:apply-templates select="/item"/> 
  </xsl:template> 
  <xsl:template match="item">
    <xsl:for-each select="data">
      <xsl:value-of select="@value"/>
      <xsl:if test="not(position()=last())">,</xsl:if>
    </xsl:for-each>
    <xsl:text disable-output-escaping = "yes">&#13;&#10;</xsl:text>
  </xsl:template>
</xsl:stylesheet>

xsl:sort

xsl:apply-templates や xsl:for-each の後に xsl:sort 要素を入れると、処理をノー ド順ではなく、整列させることができます。 select 属性でキーを指定しますが、 data-type 属性で text か number を、 order 属性で ascending か descending か、 case-order で upper-first か lower-first かを指定できます。

例11-10

下記のようにすると、単価が高い順に並べます。


<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "html"
     indent = "yes"
     doctype-public = "-//W3C//DTD HTML 4.01//EN"
     doctype-system = "http://www.w3.org/TR/html4/strict.dtd" />
  <xsl:template match="/"> 
    <html lang="ja">
      <head>
	<title>Test</title>
      </head>
      <body>
	<xsl:apply-templates select="//item">
	  <xsl:sort select="data[@name='単価']/@value"
		    data-type="number" order="descending"/>
	</xsl:apply-templates>
      </body>
    </html>
  </xsl:template> 
  <xsl:template match="item">
    <dl>
      <xsl:apply-templates/>
    </dl>
  </xsl:template>
  <xsl:template match="data"> 
    <dt><xsl:value-of select="@name"/></dt>
    <dd><xsl:value-of select="@value"/></dd>
  </xsl:template> 
</xsl:stylesheet>
品名
もも
単価
300
個数
1
合計
301
品名
りんご
単価
200
個数
3
合計
600
品名
みかん
単価
100
個数
5
合計
500

xsl:number

xsl:number は自動的な番号づけを行います。 format で書式を指定する他、 level で複数の番号を管理したりもできます。


<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "html"
     indent ="yes"
     doctype-public = "-//W3C//DTD HTML 4.01//EN"
     doctype-system = "http://www.w3.org/TR/html4/strict.dtd"
/>
  <xsl:template match="/itemlist"> 
    <html lang="ja">
      <head>
	<title>Test1</title>
      </head>
      <body>
	<table border="1">
	  <thead>
	    <tr>
	      <th>番号</th>
	      <xsl:apply-templates select="item[1]/data" mode="head"/>
	    </tr>
	  </thead>
	  <tbody>	
	    <xsl:apply-templates select="item" mode="data"/>
	  </tbody>
	</table>
      </body>
    </html>
  </xsl:template> 
  <xsl:template match="data" mode="head" >
  <th><xsl:value-of select="@name"/></th>
  </xsl:template>
  <xsl:template match="item" mode="data">
    <tr>
      <th>
	<xsl:number format="1."/>
      </th>
      <xsl:apply-templates select="data" />
    </tr>
  </xsl:template>
  <xsl:template match="data"> 
    <td>
      <xsl:value-of select="@value"/>
    </td>
  </xsl:template> 
</xsl:stylesheet>
番号品名単価個数合計
1.りんご2003600
2.みかん1005500
3.もも3001301

11-4. XSLTによるプログラミング

xsl:variable と xsl:param

xsl:variable と xsl:param はともに変数を指定します。 name 属性に変数名を入れ、 select 属性、またはテキストノードとして変数 の値を指定します。 xsl:variable は再代入できません。 一方、 xsl:param はtemplate の中で定義しておくと、 apply-templates の 呼び出し時に xsl:with-param 要素で指定することで、値を上書きできます。 つまり、同一の template 内では再代入できませんが、 template 呼び出しの 時に再代入することができます。

なお、変数の値は 「$変数名」により XPath 式の中に記述できます。

xsl:call-template

call-template は name 属性で名前のついている template をその名前で呼び 出します。 上記の param とこの call-template を使用すると、 template を関数として 使用することができます。

例11-11

再帰呼び出しによって階乗を計算するスタイルシートを示します。


<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "html"
     indent = "yes"
     doctype-public = "-//W3C//DTD HTML 4.01//EN"
     doctype-system = "http://www.w3.org/TR/html4/strict.dtd"
     />
  <xsl:template match="/"> 
    <html lang="ja">
      <head>
	<title>Test</title>
      </head>
      <body>
	<ol>
	  <xsl:for-each select="//data">
	    <li>
	      <xsl:value-of select="text()"/>! = 
	      <xsl:call-template name="factorial">
		<xsl:with-param name="val" select="number(text())"/>
	      </xsl:call-template>
	    </li>
	  </xsl:for-each>
	</ol>
      </body>
    </html>
  </xsl:template> 
  <xsl:template name="factorial">
    <xsl:param name="val" select="0"/>
    <xsl:param name="result" select="1"/>
    <xsl:choose>
      <xsl:when test="$val=0">
	<xsl:value-of select="$result"/>
      </xsl:when>
      <xsl:otherwise>
	<xsl:call-template name="factorial">
	  <xsl:with-param name="result" select="$result * $val"/>
	  <xsl:with-param name="val" select="$val - 1"/>
	</xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>	
  </xsl:template>
</xsl:stylesheet>

apply-templates の選択

apply-templates で複数マッチする template がある場合、もっとも範囲の少 ないものが選ばれます。 これを利用すると、通常の処理に対して、ある特殊な場合には特別な処理をし たいような場合、その特別な処理を単に付け足すだけで済みます。 これは C++ の template 計算に行われる specialization と同じような考え 方です。

例11-12

例11-8 で示したコードと同等で、この複数の template の選択ルールを 使用する例を示します。


<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "html"
     indent = "yes"
     doctype-public = "-//W3C//DTD HTML 4.01//EN"
     doctype-system = "http://www.w3.org/TR/html4/strict.dtd" />
  <xsl:template match="/"> 
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
      <head>
	<title>一次方程式</title>
      </head>
      <body>
	<ol>
	<xsl:apply-templates select="//houteishiki"/>
	</ol>
      </body>
    </html>
  </xsl:template> 
  <xsl:template match="houteishiki">
    <li>
      <p>
	<xsl:if test="keisuu[1]/text() != 1">
	  <xsl:value-of select="keisuu[1]/text()"/>
	</xsl:if>
	<var>x</var>
	<xsl:if test="keisuu[2]/text() >= 0">
	  +
	</xsl:if>
	<xsl:value-of select="keisuu[2]/text()"/>
	  =0 の解は、
	<xsl:apply-templates select="." mode="kai"/>
      </p>
    </li>
  </xsl:template>
  <xsl:template match="houteishiki" mode="kai">
    <var>x</var> =
    <xsl:value-of select="- keisuu[2]/text() div keisuu[1]/text()"/>
    です。
  </xsl:template>
  <xsl:template match="houteishiki[keisuu[1]=0]" mode="kai">
    ありません。
  </xsl:template>
  <xsl:template match="houteishiki[keisuu[1]=0 and keisuu[2]=0]" mode="kai">
    任意の数です。
  </xsl:template>
</xsl:stylesheet>

カレントノードとループ変数

template の内部で XPath を使う際、式はカレントノードを起点に、あるいは、 template の match 属性で指定したノードを絶対パスの起点として使用できま す。 したがって、 template の呼び出しをすると、 カレントノード並びに絶対パスは切り替わります。 一方、 for-each でもカレントノードは select 内の式に切り替わりますが、 絶対パスの起点は切り替わりません。

さて、特定のリストを参照して、そのリストと照合しながら処理を行うような ことを考えます。 その時、「リストの各値に対して、ノードセットの処理を行う」ことになりま す。 しかし、同一 template の中では再代入可能な変数はありませんから、単独の template ではループ変数のようなものも作れません。 ループ変数を作るには、 for-each で作成した値を with-param に渡して 別の template に送る方法が取れます。 ただし、別の template に送る場合、絶対パスの起点も切り替わってしまうの で、必要なノードセットを別の with-param に渡す必要があります。

そのため、この二つの値を処理させるため、with-param で二つの値を指定し て、template 呼び出しを行います。


<xsl:template match="???">
...
  <xsl:variable name="list" select="..."/>
  <xsl:for-each select="$list">
    <xsl:call-template name="sub1">
      <xsl:with-param name="each" select="."/>
      <xsl:with-param name="nodeset" select="/絶対パス指定によるノードセットの指定"/>
    </xsl:call-template>
  <xsl:for-each select="$list">
</xsl:template>
<xsl:template name="sub1">
  <xsl:param name="each" />
  <xsl:param name="nodeset"/>
  <xsl:for-each select="$nodeset">
    <xsl:value-of select="???[@name=$each]/@value"/>
  </xsl:for-each>
<xsl:/template>

例11-13

今までの例では itemlist.xml 中の data 要素が順番に並んでいると仮定して、 様々な処理を行って来ました。 しかし、本来は name 属性によってデータは結びつけられています。 そのため、順番が異なっても同じ変換結果にならないとおかしいです。 ここでは上記のテクニックを使用して、名前ごとの処理を実現します。


<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
  <xsl:output
     method = "html"
     indent ="yes"
     doctype-public = "-//W3C//DTD HTML 4.01//EN"
     doctype-system = "http://www.w3.org/TR/html4/strict.dtd"
/>
  <xsl:template match="/itemlist"> 
    <xsl:variable name="header" select="item[1]/data/@name"/>
    <html lang="ja">
      <head>
	<title>Test1</title>
      </head>
      <body>
	<table border="1">
	  <thead>
	    <tr>
	      <xsl:for-each select="$header">
		<th>
		  <xsl:value-of select="."/>
		</th>
	      </xsl:for-each>
	    </tr>
	  </thead>
	  <tbody>	
	    <xsl:for-each select="item">
	      <xsl:call-template name="sub1">
		<xsl:with-param name="head" select="$header"/>
		<xsl:with-param name="node" select="."/>
	      </xsl:call-template>
	    </xsl:for-each>
	  </tbody>
	</table>
      </body>
    </html>
  </xsl:template> 
  <xsl:template name="sub1">
    <xsl:param name="head" />
    <xsl:param name="node"/>
    <tr>
      <xsl:for-each select="$head">
	<td>
	  <xsl:call-template name="sub2">
	    <xsl:with-param name="index" select="."/>
	    <xsl:with-param name="node" select="$node"/>
	  </xsl:call-template>
	</td>
      </xsl:for-each>
    </tr>
  </xsl:template>
  <xsl:template name="sub2">
    <xsl:param name="index" />
    <xsl:param name="node"/>
    <xsl:value-of select="$node/data[@name=$index]/@value"/>
  </xsl:template>
</xsl:stylesheet>

11-5. XSLT のその他の機能

XSLT のその他の機能として、名前空間の管理や、他のドキュメントや XSLTス タイルシートの呼び出しなどがあります。 また、 key 関数は特定の属性を検索することができます。

11-6. 付録

Dentaku

標準入力から与えられた XML に対し、第一引数を XSLT を含むファイル名、 第二引数を XML 文書とし、結果を出力するプログラムを示します。

実行方法はjava Dentaku XSLTファイル名 XML文書ファイル名です。


import java.io.*;
import java.util.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*; 
class Dentaku {
    private static Properties getProperties(){
	Properties prop = new Properties();
	prop.setProperty("encoding", "Shift_JIS");
	return prop;
    }
    private static Transformer getTransformer(StreamSource ss)
                                                   throws Exception {
      TransformerFactory factory = TransformerFactory.newInstance();
      Transformer transformer = factory.newTransformer(ss);
      transformer.setOutputProperties(getProperties());
      return transformer;
    }	
    public static void main(String[] args) throws Exception {
	getTransformer(new StreamSource(new FileInputStream(args[0])))
	    .transform(new StreamSource(new FileInputStream(args[1])),
		       new StreamResult(System.out));
    }
}

坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科