読者がUNIXユーザーであれば、パイプ の概念をご存じでしょう。これは、1つのプログラムの出力を別のプログラムの入力にするメカニズムです。パイプはおそらく、疎結合されたコードをモジュラー化する初期の事例の主な基礎になっていたと思われます。それぞれのUNIXコマンドは非常にシンプルで目標がはっきりしています。複雑なアクションは、そのようなコマンドを複数組み合わせることによって生み出されます。XSLTを使ったXMLの処理もまた、これと同様のモジュラー化によって大きく効率化されます。
変換を複数のフェーズあるいはパス (pass) からなるセットに分割すれば、コードをより簡潔にできるとともに、再利用も促進されます。残念ながら、純粋なXSLT 1.0では、変換の入力を処理するほとんどのコマンドは出力を扱うことができません。この制限はXSLT 2.0では取り除かれました。しかし、(これから何年も普及するであろう) XSLT 1.0でさえ、通常XSLTベンダーによって提供される拡張機能を利用すれば、こうした制限を取り除くことができます。
このヒントを読み進めるには、XSLTに関する知識が必要です。
これから例示する小さなXSLTテンプレートは、文書テーブル (表) を入力として、各行の最初の項目だけを表示します。このテンプレートは、(SGMLで普及しているテーブル・モデルに基づく) DocBookのようなテーブルを処理するよう意図されています。サンプル・テーブル (db-table.xml) がリスト1 に示されています。
リスト1. DocBook形式の簡単なテーブル (db-table.xml)
<table frame="all">
<title>Numbers and tongues (数と言語)</title>
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<thead>
<row>
<entry>1</entry>
<entry>2</entry>
<entry>3</entry>
</row>
</thead>
<tfoot>
<row>
<entry>I</entry>
<entry>II</entry>
<entry>III</entry>
</row>
</tfoot>
<tbody>
<row>
<entry>one</entry>
<entry>two</entry>
<entry>three</entry>
</row>
<row>
<entry>uno</entry>
<entry>dos</entry>
<entry>tres</entry>
</row>
<row>
<entry>otu</entry>
<entry>abuo</entry>
<entry>ato</entry>
</row>
</tbody>
</tgroup>
</table>
|
リスト2 (db-onecol.xslt) は、テーブルの第1列だけを表示させる変換です。
リスト2. DocBook形式のテーブル (db-onecol.xslt) の第1列だけを表示させるXSLT変換
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common"
version="1.0"
>
<xsl:output method="text"/>
<xsl:template match="table">
<xsl:value-of select="title"/><xsl:text> </xsl:text>
<xsl:for-each select="tgroup/thead/row">
<xsl:value-of select="entry[1]"/><xsl:text> </xsl:text>
</xsl:for-each>
<xsl:for-each select="tgroup/tbody/row">
<xsl:value-of select="entry[1]"/><xsl:text> </xsl:text>
</xsl:for-each>
<xsl:for-each select="tgroup/tfoot/row">
<xsl:value-of select="entry[1]"/><xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:transform>
|
これによる出力は、単純なテキストです。エンティティー は、テキストが空白文字としてスタイルシートから除去されないようにするために、xsl:text に挿入される改行です。残りの部分は単純です。table要素が検出されれば、titleが出力となり、続いてテーブル・ヘッド (thead)、本文 (tbody)、フッター (tfoot) の各行の最初の項目だけが出力されます。ここでは、tgroup/*/row を使用したただ1つのxsl:for-each という単純な実装にはしませんでした。文書内ではthead、tbody、tfoot の各要素が任意の順番で現れる可能性がありますが、私はこれらを特定の順序で処理させたかったからです。以下のセッションは、この変換が実行される様子を示しています。
$ 4xslt db-table.xml db-onecol.xslt Numbers and tongues 1 one uno otu I |
さて、リスト3 (xhtml-table.xml) のようなXHTML形式のテーブルを同じように処理してみましょう。
リスト3. XHTML形式のテーブル (xhtml-table.xml)
<table border="1" frame="box">
<caption>Numbers and tongues</caption>
<thead>
<tr>
<th>1</th>
<th>2</th>
<th>3</th>
</tr>
</thead>
<tbody>
<tr>
<td>one</td>
<td>two</td>
<td>three</td>
</tr>
<tr>
<td>uno</td>
<td>dos</td>
<td>tres</td>
</tr>
<tr>
<td>otu</td>
<td>abuo</td>
<td>ato</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>I</td>
<td>II</td>
<td>III</td>
</tr>
</tfoot>
</table>
|
このテーブルは要素名が異なり、編成も少し異なっているので、DocBookテーブル・テンプレートを単純に再使用することはできません。もちろん、このテンプレートをコピーして少しばかり修正を加えれば、XHTML要素用の特別なバージョンを作成できます。しかし、これはあまりモジュラー的な方法ではありません。別の方法は、XHTMLをDocBook形式に変換して、それをDocBookテンプレートにかけることです。こうすることの利点は、いったん変換が完成すれば、DocBookテーブル用の他の機能を再利用できることです。
リスト4 (xhtml-onecol.xslt) は、DocBookテーブル・モジュールでXHTMLテーブルを扱えるようにするための変換です。
Listing 4. XHTML形式のテーブル (xhtml-onecol.xslt) の第1列だけを表示させるXSLT変換
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common"
version="1.0"
>
<xsl:import href="db-onecol.xslt"/>
<xsl:template match="/">
<xsl:apply-templates mode="xhtml"/>
</xsl:template>
<xsl:template match="table" mode="xhtml">
<xsl:variable name="db-table">
<xsl:call-template name="xhtml-table-to-db"/>
</xsl:variable>
<xsl:apply-templates
select="exslt:node-set($db-table)/table"/>
</xsl:template>
<xsl:template name="xhtml-table-to-db">
<xsl:copy>
<title><xsl:value-of select="caption"/></title>
<tgroup cols="{count(thead/tr/th)}">
<thead>
<row>
<xsl:for-each select="thead/tr/th">
<entry><xsl:apply-templates/></entry>
</xsl:for-each>
</row>
</thead>
<tfoot>
<row>
<xsl:for-each select="tfoot/tr/td">
<entry><xsl:apply-templates/></entry>
</xsl:for-each>
</row>
</tfoot>
<tbody>
<xsl:for-each select="tbody/tr">
<row>
<xsl:for-each select="td">
<entry><xsl:apply-templates/></entry>
</xsl:for-each>
</row>
</xsl:for-each>
</tbody>
</tgroup>
</xsl:copy>
</xsl:template>
</xsl:transform>
|
ここで重要なことが1つあります。要点を目立たせるために、私はこれらの例を意図的に単純化しました。これらのスタイルシートは、(テンプレートやモードを多用する)プッシュ ・スタイルのXSLTではなく、(xsl:for-each およびxsl:value-of を多用する)プル ・スタイルのXSLTを使用しています。このようにした理由は、プッシュ・スタイルの方が多くの点で優れているにもかかわらず、プル・スタイルの方がより普及しているからです。たとえば、実際のプロジェクトでは、自分自身の変換のバリエーション (変種) としてXHTMLをDocBookに変換するためのテンプレートを書くでしょう。さらに、XHTMLおよびDocBookテーブルのさまざまな一般的ケースを扱うには、より多くの論理をテンプレートに含める必要があるでしょう。
マルチパス技法の最も重要な点は、以下の行です。
<xsl:apply-templates select="exslt:node-set($db-table)/table"/> |
これは、1つのフェーズから次のフェーズへのハンドオフ (引き継ぎ) です。最初のパスでは、変数db-table 内でXHTMLテーブルがDocBook形式に変換されます。この結果として生成される出力のツリー断片は、リスト1 に非常に似ています。これを2番目のパスの入力として扱うには、結果ツリー断片からノード・セットに変換する必要があります。exslt:node-set は、まさにこの機能を実行します。この拡張機能はいくつかのプロセッサーでサポートされています。EXSLT拡張機能をサポートしないプロセッサーでさえも、これと同様に機能する独自のノード・セット機能が提供されている場合がほとんどです。
この新しいノード・セットからテーブル要素を選択して、2番目のパスで利用します。2番目のパスでは、インポートされたdb-onecol.xslt モジュールからのテーブル・テンプレートが処理を行います。ここでは、モード (xhtml) を使ってXHTMLテーブルを選択します。こうすれば、このテンプレートは、マッチングが同等でインポート優先順位のより低いDocBookテンプレートの操作を妨害しません。
この変換の出力は、純粋なDocBookテーブルの変換と同じです。私のまったく意図したとおりに、DocBookコードを再利用することができました。
今回の例は、実際のプロジェクトで私が遭遇した状況を極端に単純化したものです。XHTMLソース処理のために多数のDocBook処理テンプレートを再利用する必要がありました。第1のパスでXHTMLの内容をDocBookに変換し、後続のパスで標準的なDocBookテンプレートを再利用することによって、私は多くの作業とデバッグを節約することができました。マルチパスXSLTの概念は、これよりも一般的です。コードの再利用が促進されるのに加えて、複雑な変換処理をわかりやすい断片に分けることができます。読者がこの次に煩雑なXSLTの問題にぶつかったら、それを複数のパイプからなる処理として単純化あるいはモジュラー化できないかどうか、ぜひ検討してください。
- W3CのXSL ページをご覧ください。ここには、仕様そのもの、チュートリアル、記事、インプリメンテーションなど、XSLT関連の役立つリソースへのリンクが多数あります。
- XSLTの初歩に関する優れた記事、"Investigating XSLT: The XML transformation language" (LindaMay Patterson著、developerWorks 2001年8月) をお読みください。
- 著者Uche Ogbujiによる他のXSLT関連ヒント:ノード・セットによるカウント (developerWorks 2002年5月)、XSLTを使った内部HTMLリンクの生成 (developerWorks 2001年2月)、さらにXSLTによるルックアップ・テーブル (developerWorks 2001年2月) をご覧ください。
- 今回の例で使われたスタイルシート・プロセッサー4XSLTは、Uche Ogbuji氏が共同開発者となった4Suite の一部です。
- XSLT関連の、役立つ、広くサポートされた拡張機能について、EXSLT をご覧ください。common モジュールには、ノード・セットのほかに多数の機能があります。
- すべてのXSLTユーザーは、自分自身の変換 (identity transform) について知っておく必要があります。自分自身の変換のカスタマイズについては、Jason Diamond氏による記事Template Languages in XSLT をお読みください。
- DocBookについてDocBook.org をご覧ください。さらに、"DocBookの概要 (Joe Brockmeier著、(developerWorks 2000年9月) もご覧ください。
- XHTMLに関する記事、たとえばXHTML: The power of two languages (Sathyan Munirathinam著、developerWorks 2002年7月)、The Web's future: XHTML 2.0 (Nicholas Chase著、developerWorks 2002年9月)、Introduction to XHTML, with eXamples (Alan Richmond著) などをお読みください。
-
developerWorks XMLゾーンにはXMLについての情報が満載されています。
-
XMLおよび関連テクノロジーに関するIBM認定デベロッパー になる方法をご覧ください。
- この記事のようなXMLのヒントを毎週受け取りたい読者は、developerWorksXML Tipsニュースレターに参加してください。

Uche Ogbuji氏は、Fourthought, Inc. のコンサルタント兼共同設立者です。この会社は、企業のナレッジ・マネジメントのためのXMLソリューションを専門とするソフトウェア・ベンダー兼コンサルタント会社です。Fourthoughtでは、XML、RDF、およびナレッジ・マネジメント・アプリケーション用のオープン・ソース・プラットフォームである4Suiteを開発しています。Ogbuji氏は、ナイジェリア出身のコンピューター・エンジニア兼ライターで、現在は、米国コロラド州ボールダーに住み、そこで働いています。Ogbuji氏の連絡先はuche.ogbuji@fourthought.com です。