レベル: 初級 Jack Herrington (jherr@pobox.com), Editor-in-Chief, Code Generation Network
2005年 3月 18日 ある程度複雑なデータ・セットでは、ナビゲートするために複数のビューが必要です。例えばQAテストのシステムを考えてみてください。テストやテスト結果のプールの中から、日付毎、テスト・カテゴリー毎、個々のテスト毎、などによってデータを見る必要があります。それぞれのビューは、それぞれ独自のHTMLファイルになっています。では、XSLT 2.0での単一テンプレートを使って、1つの入力データ・セットから複数のHTMLファイルを作ることができるのでしょうか?
XSLTの最初のバージョンは非常に厳密でした。1つの入力と、1つの出力しか無いのです(ただし、1つ以上のテンプレート・ファイルを持つことはできました)。XSLT 2.0では、入力は相変わらず1つに制限していますが、出力システムは、もっと柔軟になっています。XSLT 2.0では、xsl:result-documentディレクティブを使って、複数の出力ファイルを持つことができます。この新しいタグには2つ、重要な属性があります。これを表1に示します。
表1. xsl:result-documentの属性
| 属性 | 説明 |
|---|
href
| ファイル名、または完全修飾された出力ファイルのURL |
format
| 使うべきフォーマットの名前であり、対応するxsl:outputディレクティブで定義されます |
私はこのディレクティブをテストするために、一連のテスト結果を含む1つのXMLファイルを用意しました(リスト1)。
リスト1. 入力XMLファイル
<?xml version="1.0" encoding="UTF-8"?>
<tests>
<testrun run="test1">
<test name="foo" pass="true" />
<test name="bar" pass="true" />
<test name="baz" pass="true" />
</testrun>
<testrun run="test2">
<test name="foo" pass="true" />
<test name="bar" pass="false" />
<test name="baz" pass="false" />
</testrun>
<testrun run="test3">
<test name="foo" pass="false" />
<test name="bar" pass="true" />
<test name="baz" pass="false" />
</testrun>
</tests>
|
これは、ごく単純なものです。各テスト実行の中には名前の付いた一連のテストがあり、それぞれが、テストが成功したかどうかを示すパス・フラグを持っています。
各テスト用にファイルを作る
まず、最初にすべきことは、各テスト結果に対してファイルを作ることです。リスト2はXSLテンプレートを示しています。
リスト2. 各テストに対してファイルを作るコード
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text"/>
<xsl:output method="html" indent="yes" name="html"/>
<xsl:template match="/">
<xsl:for-each select="//testrun">
<xsl:variable name="filename"
select="concat('output1/',@run,'.html')" />
<xsl:value-of select="$filename" /> <!-- Creating -->
<xsl:result-document href="{$filename}" format="html">
<html><body>
<xsl:value-of select="@run"/>
</body></html>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
|
まずファイルの先頭から、幾つか、注意すべき事があります。スタイルシート・タグのversion属性は、xsl:result-documentタグが使えるように、2.0に設定されます。その後を見ると、スタイルシート自体は、出力タイプとしてtextに設定されていることが分かります。これは、HTMLファイルがHTMLフォーマットを持つようにしたいのであれば、第2の名前付きフォーマットを、htmlタイプで定義する必要がある、ということを意味します。私はこのフォーマットをxsl:result-documentタグの中で使っています。
そこからは、xsl:for-eachループを使って、testrunタグまで繰り返します。こうしたタグのそれぞれに対し、variableタグを使って新しい$filename変数を作り、出力ディレクトリー名(output1)と実行名、そして .htmlファイルを1つのパスに連結させます。
こうした上で、$filename変数を持つvalue-ofタグを使って、私がどんなファイルを作っているのかをユーザーに対して伝えます。その後でxsl:result-documentタグを使って新しい文書を開き、HTMLを出力します。リスト3は、このエンジンをサンプル・データファイルに対して実行した場合の出力を示しています。
リスト3. Saxonをサンプル・データファイルに対して実行した場合の出力
Creating output1/test1.html
Creating output1/test2.html
Creating output1/test3.html
|
より良い出力を得る
xsl:result-documentタグの中身が、このテンプレートの他の部分と同じように評価されるのは、少し直感に反するように思えるかもしれませんが、実際、同じように評価されるのです。そして、テンプレート・コンテキスト内の変数が全て利用できるのです。
これを示すために、xsl:result-documentタグ内のコードをアップグレードし、テスト結果に関する情報を、より多く示すようにします(リスト4)。
リスト4. テンプレートからの、改善されたHTML出力
<xsl:result-document href="{$filename}" format="html">
<html><head>
<title>Test results - <xsl:value-of select="@run"/></title>
</head><body>
<xsl:value-of select="@run"/> <!-- Run -->
<table>
<tr><td>Test</td><td>Pass</td></tr>
<xsl:for-each select="test">
<tr><td>
<xsl:value-of select="@name" />
</td><td>
<xsl:value-of select="@pass" />
</td></tr>
</xsl:for-each>
</table>
</body></html>
</xsl:result-document>
|
この出力のHTML結果をリスト5に示します。
リスト5. アップグレードされたHTML出力
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test results - test1</title>
</head>
<body>
<!-- Run: test1-->
<table>
<tr>
<td>Test</td>
<td>Pass</td>
</tr>
<tr>
<td>foo</td>
<td>true</td>
</tr>
<tr>
<td>bar</td>
<td>true</td>
</tr>
<tr>
<td>baz</td>
<td>true</td>
</tr>
</table>
</body>
</html>
|
インデックスを作る
最後に、出力テスト結果それぞれを指す、インデックス・ファイルを追加します。そのために、私は別のxsl:result-documentタグを使って出力をハードコード化し、インデックス・ファイルになるようにします(リスト6)。
リスト6. インデックス・ファイルを作るコード
<!-- Creating the index -->
<xsl:result-document href="output3/index.html" format="html">
<html><head><title>Test Index</title></head>
<body>
<xsl:for-each select="//testrun">
<a href="{@run}.html"><xsl:value-of select="@run" />
</a><br/>
</xsl:for-each>
</body>
</html>
</xsl:result-document>
|
このセクションは、各テスト・ケースに対してHTMLファイルを作るxsl:for-eachループの直後に置きます。リスト7は、サンプルのデータ・セットに対するインデックス・ファイルを示しています。
リスト7. インデックス・ファイル
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<title>Test Index</title>
</head>
<body><a href="test1.html">test1</a><br>
<a href="test2.html">test2</a><br>
<a href="test3.html">test3</a><br></body>
</html>
|
まとめ
xsl:result-documentディレクティブを使うことにより、1つのデータ・ソースから複数のファイルを、1つのXSLテンプレートを使って出力できるようになります。この機能はXSLT 1.xの非標準的拡張でしたが、XSLTテンプレートを書く人達にとって、新たな世界を開くものです。
参考文献
著者について  | |  | Jack D. Herringtonは、20年以上の経験を持つシニア・ソフトウェア・エンジニアです。著者には、「Code Generation in Action」、「Podcasting Hacks」、そして近々刊行予定の「PHP Hacks」の3冊があります。彼は30本以上の技術記事も執筆しています。 |
記事の評価
|