目次


XSLTのコーディングを改善する5つの方法

より良いXSLTプログラマーになるためのヒント

Comments

XMLとXSLTの組み合わせは、中規模から大規模のWebサイトのWebマスターの間で人気が高まっています。XSLTが登場する前、Webサイトの表示を変えるのは大仕事でした。再度足を運んで、サイトのすべてのページを変更しなければなりませんでした。XSLTを使えば、このプロセスを自動化して、時間を大幅に節約できます。

ヒント1: Cascading Style Sheets、表、およびXSLT

XSLTの記事の冒頭でCSSに関するヒントを示すのは奇妙に思えるかもしれません。しかし、しばしば尋ねられる質問として、「これらの2つのスタイル・シート言語には互換性があるのか」というものがあります。答えははっきりしています。確かに互換性があります。

この点はリスト1に示されています。これは、XMLで書いた製品リストである、products.xmlを示したものです。時間をとってproducts.xmlに精通してください。5つのヒントすべてに渡ってこの例を使用するからです。

リスト1. products.xml (XMLによる製品リスト)
<?xml version="1.0"?>
<products>
   <product href="http://www.playfield.com/text">
      <name>Playfield Text</name>
      <price currency="usd">299</price>
      <description>Faster than the competition.</description>
      <version>1.0</version>
   </product>
   <product href="http://www.playfield.com/virus">
      <name>Playfield Virus</name>
      <price currency="eur">199</price>
      <description>Protect yourself against malicious code.</description>
      <version>5.0</version>
   </product>
   <product href="http://www.playfield.com/calc">
      <name>Playfield Calc</name>
      <price currency="usd">299</price>
      <description>Clear picture on your data.</description>
      <version>1.5</version>
   </product>
   <product href="http://www.playfield.com/db">
      <name>Playfield DB</name>
      <price currency="cad">599</price>
      <description>Organize your data.</description>
   </product>
</products>

この文書をHTMLでフォーマットするには、リスト2のtable.xsl が使えるかもしれません。table.xslをproducts.xml に適用すると、図1 のようになります。ご覧のとおり、1行おきに背景色がグレーになって、読みやすくなっています。table.xslでは、これをCascading Style Sheetsを使用して実現しています。

リスト2. table.xsl (CSSを使用したXSLTスタイル・シート)
<?xml version="1.0"?>
<xsl:stylesheet
   version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"indent="no"/>
<xsl:template match="/products">
   <html>
      <head>
         <title>Cascading Style Sheet</title>
         <link rel="stylesheet" type="text/css" href="table.css" title="Style"/>
      </head>
      <body>
        <table>
          <tr class="header">
             <td>Name</td>
             <td>Price</td>
             <td>Description</td>
          </tr>
          <xsl:apply-templates/>
        </table>
      </body>
   </html>
</xsl:template>
<xsl:template match="product[position() mod 2 = 1]">
   <tr class="odd">
      <td><xsl:value-of select="name"/></td>
      <td><xsl:value-of select="price"/></td>
      <td><xsl:value-of select="description"/></td>
   </tr>
</xsl:template>
<xsl:template match="product">
   <tr class="even">
      <td><xsl:value-of select="name"/></td>
      <td><xsl:value-of select="price"/></td>
      <td><xsl:value-of select="description"/></td>
   </tr>
</xsl:template>
</xsl:stylesheet>
図1. HTMLによる製品リスト
product list in table format with dark shaded header and alternate shaded rows
product list in table format with dark shaded header and alternate shaded rows

これはどのように機能しているのでしょうか。table.xsl スタイル・シートは、出力にHTML<link> 要素を挿入します。<link> はCascading Style Sheetsをロードします。

<link rel="stylesheet" type="text/css" href="table.css" title="Style"/>

CSSとXSLTは同時に使用されるわけではないので、競合は生じません。まずXSLTスタイル・シートが、XML文書であるproducts.xml に適用されます。結果としてHTML文書が生成されて、これがブラウザーに渡されます。HTML文書がCascading Style Sheetsをロードしていますが、これはこの段階では関係がありません。CSSを使用すべきなのは、ブラウザーがHTML文書をロードするときです。

CSSを利用するため、XSLTスタイル・シートtable.xsl はさらに、適切な箇所にclass 属性を挿入します。実際には、表内の要素にはodd またはeven のクラスのどちらかのマークが付けられます。どのようにXSLTから奇数の行を認識できますか。リスト2のスタイル・シートから抜粋した以下の行が示すとおり、match 属性に条件を追加することによってです。

<xsl:template match="product[position() mod 2 = 1]">
   <tr class="odd">
      <td><xsl:value-of select="name"/></td>
      <td><xsl:value-of select="price"/></td>
      <td><xsl:value-of select="description"/></td>
   </tr>
</xsl:template>

リスト3に示す、table.css という単純なCascading Style Sheetsは、表の偶数の行の背景色をグレーにします。

リスト3. table.css (図1の表のCSS)
.header { background-color: #999999; font-weight: bold; }
.odd { background-color: normal; }
.even { background-color: #dfdfdf; }

どのような場合にCSSとXSLTを組み合わせたらよいのでしょうか。組み合わせるのが適当と思われるのは、次の場合です。

  • 表示をより強力に制御する場合。CSSは生のHTMLよりも強力だからです。
  • 作成されるHTMLをより小さくして、ダウンロードの時間を短縮する場合。

しかし、両方のスタイル・シートを組み合わせても、保守容易性はあまり向上しないことに注意してください。確かに、もともと設計者がCSSに魅力を感じたのは、1つのファイルでWebサイト全体の設計を制御できるという点でした。その点で言えば、CSSはXSLTと重複します。複数の文書を一度に再フォーマットすることは、すでに容易であるからです。

注意の必要な点があります。table.xsl、およびこの記事で取り上げる他のスタイル・シートには、標準規格に準拠したXSLTプロセッサーが必要です。Internet Explorer 5.0および5.5に付属のプロセッサーは、標準規格に準拠したものではありません。準拠プロセッサーが必要な場合は、ApacheプロジェクトのXSLTプロセッサーであるXalanを使ってみるか、IEのプロセッサーをバージョン3.0にアップグレードしてみてください (参考文献参照)。

ヒント2: HTML実体

開発者がXSLTスタイル・シートの作成について尋ねる一般的な質問のもう1つの例は、どのようにHTML実体を挿入するかというものです。特に、&nbsp; 実体 (改行なしスペース) をどのように挿入するかについてです。&nbsp; は、とりわけ表の中に空でないセルを作成する目的で使用されます。

残念ながら、次の自明の解決策は役に立ちません。

<tr>&nbsp;</tr>

なぜでしょうか。それはXSLTスタイル・シートがXML文書だからです。XSLTスタイル・シートを構文解析すると、実体は解決されます。&nbsp; 実体はXML実体として解決されます。&nbsp; はXMLでは定義されていないため、結果としてエラーになるのです。

解決策をリスト4のnbsp.xsl に示します。関係のある要素を赤で強調表示します。お分かりのとおり、HTMLで実体を書くと文字数が多くて、実体が必要になるたびにこれを入力するのは大変に思えるかもしれません。しかし、いったんコードを理解したなら、適当な場所にこれを切り貼りすれば十分です。

リスト4. nbsp.xsl (スタイル・シートにHTML実体を挿入する方法)
<?xml version="1.0"?>
<xsl:stylesheet
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="1.0">
<xsl:output method="html" indent="no"/>
<xsl:template match="/">
   <html>
      <head><title>HTML Entities</title></head>
      <body>
         <table border="1">
            <tr>
               <td>Name</td>
               <td>Price</td>
               <td>Description</td>
               <td>Version</td>
            </tr>
            <xsl:apply-templates/>
         </table>
      </body>
   </html>
</xsl:template>
<xsl:template match="product[version]">
   <tr>
      <td><xsl:value-of select="name"/></td>
      <td><xsl:value-of select="price"/></td>
      <td><xsl:value-of select="description"/></td>
      <td><xsl:value-of select="version"/></td>
   </tr>
</xsl:template>
<xsl:template match="product">
   <tr>
      <td><xsl:value-of select="name"/></td>
      <td><xsl:value-of select="price"/></td>
      <td><xsl:value-of select="description"/></td>
      <td><xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text></td>
   </tr>
</xsl:template>
</xsl:stylesheet>

改行なしスペースの実体の正しいコードは、以下のリスト4からの抜粋部分が示すとおりです。

<xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>

このコード行で何が行われるのでしょうか。秘けつは、<xsl:text> 要素をdisable-output-escaping="yes" 属性と併用することにあります。<xsl:text> により、出力HTMLにテキストが作成されます。属性は、テキストの内容をエスケープしないよう、プロセッサーに命じます。テキスト自体は単純な&nbsp; というものです。& 文字はXMLでエスケープされます。

スタイル・シートはXML文書として読み取られ、&amp;nbsp;&nbsp; になります。プロセッサーに出力HTMLで& をエスケープしないように命じると、結果として&nbsp; と書き出されます。

ヒント3: 複数の入力文書

典型的なXSLTスタイル・シートは、あるXML文書を別のXML文書、またはHTML文書に変換します。場合によっては、これでは制限が大きすぎます。(逆の事態を扱う方法については、複数の出力文書という補足記事を参照してください。)

たとえば、products.xml<price> 要素にcurrency 属性が指定されていることに注意してください。通貨ははっきりとは書かれていないものの、コードの形で書かれています (たとえば、米ドルはusd、カナダ・ドルはcad)。こうしたコードを、表示する前に変換することができます。

世界中には100を超える通貨があり、通貨を扱うアプリケーションも多数あります。したがって、通貨のリストをXML文書自体に格納しておく必要性があります。ファイルを抽出すると、リスト5のcodes.xml のようになります。

リスト5. codes.xml (通貨コードを持ったXML文書)
<?xml version="1.0"?>
<currencies>
   <currency>
      <code>eur</code>
      <name>Euros</name>
   </currency>
   <currency>
      <code>usd</code>
      <name>Dollars</name>
   </currency>
   <currency>
      <code>cad</code>
      <name>Canadian dollars</name>
   </currency>
</currencies>

結局、ここまでのところ、この例では2つのXMLファイルを使用していることになります。すなわち、products.xmlcodes.xml です。HTML文書を作成するには、これらのファイルを組み合わせなければなりません。幸い、XSLTのおかげで、複数の入力XMLファイルを組み合わせるのは簡単です。リスト6のmulti.xsl に示すとおりです。

multi.xslには2つの重要なステップがあります。最初に、スタイル・シートが (document() 関数を使って)codes.xml を開き、これにcurrencies 変数を割り当てます。

<xsl:variable
   name="currencies"
   select="document('codes.xml')/currencies"/>

すると、スタイル・シートは変数を使ってコードのリストから情報を取り出せるようになります。当然、XPathを使用して通貨文書を照会できます。

<xsl:value-of select="$currencies/currency[code=$currency]/name"/>

リスト6. multi.xsl (複数のXML文書を組み合わせるスタイル・シート)
<?xml version="1.0"?>
<xsl:stylesheet
   version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="no"/>
<xsl:variable
   name="currencies"
   select="document('codes.xml')/currencies"/>
<xsl:template match="/">
   <html>
      <head><title>Multiple documents</title></head>
      <body>
         <table>
            <tr bgcolor="#999999">
               <td>Name</td>
               <td>Price</td>
               <td>Description</td>
               <td>Version</td>
            </tr>
            <xsl:apply-templates/>
         </table>
      </body>
   </html>
</xsl:template>
<xsl:template match="product">
   <xsl:variable name="currency" select="price/@currency"/>
   <tr>
      <td><xsl:value-of select="name"/></td>
      <td>
         <xsl:value-of select="price"/>
         <xsl:text> </xsl:text>
         <xsl:value-of select="$currencies/currency[code=$currency]/name"/>
      </td>
      <td><xsl:value-of select="description"/></td>
      <td><xsl:value-of select="version"/></td>
   </tr>
</xsl:template>
</xsl:stylesheet>

ヒント4: XSLTとクライアント側のJavaScript

XSLTは強力とはいえ、不十分な場合もあります。たとえば、JavaScript、JScript、またはVBScriptなどのクライアント側のスクリプトを使用したい場合があるかもしれません。

CSSの場合と同様、XSLTは生成されるHTMLに何ら制限を課しません。しかも、スクリプトを使用することさえできます。さらに、リスト7のjavascript.xsl が例証するとおり、XSLTからJavaScriptに値を渡すこともできます。

リスト7. javascript.xsl (クライアント側のJavaScriptを生成するスタイル・シート)
<?xml version="1.0"?>
<xsl:stylesheet
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   version="1.0">
<xsl:output method="html" indent="no"/>
<xsl:template match="/">
   <html>
      <head>
         <title>JavaScript</title>
        <script language="JavaScript"><xsl:comment>
// creates and initializes an array of product descriptions
var urls = new Array()
<xsl:for-each select="products/product">
urls[<xsl:value-of select="position()"/>] = "<xsl:value-of select="@href"/>"
</xsl:for-each>
// user function
function doSelect(i)
{
   open(urls[i])
}
         // </xsl:comment></script>
      </head>
      <body>
         <ul>
           <xsl:for-each select="products/product">
               <li><a href="javascript:doSelect({position()})">
                  <xsl:value-of select="name"/>
               </a></li>
            </xsl:for-each>
         </ul>
      </body>
   </html>
</xsl:template>
</xsl:stylesheet>

javascript.xsl を読むにあたっても、何がどこで生じているかを覚えておく必要があります。XSLTが最初に適用されて、HTMLファイルが生成されます。ファイルには<script> 要素が含まれています。その内容はXSLTによって生成されます。次に、ブラウザーがHTMLファイルをロードし、スクリプトを実行します。スクリプトを実行するのはブラウザーであって、スタイル・シートではないことを覚えておいてください。

たとえば、javascript.xsl の中の、赤で強調表示されている部分で、スタイル・シートはJavaScriptの配列を初期化しています。実際には、スクリプトに値を渡すことによってこれを行っています。

その後、スタイル・シートは関数呼び出しを生成し、再びスクリプトに値を渡します (XSLTのposition() 関数を使用)。リスト7の青で強調表示した行が示すとおりです。

リスト8は、HTMLの生成結果を示しています。このスクリプトは、ユーザーが<a> タグをクリックすると、ブラウザーによって実行されます。次に示すとおりです。

リスト8. javascript.xslによって生成されるHTML
<ul>
   <li><a href="javascript:doSelect(1)">Playfield Text</a></li>
        <li><a href="javascript:doSelect(2)">Playfield Virus</a></li>
        <li><a href="javascript:doSelect(3)">Playfield Calc</a></li>
        <li><a href="javascript:doSelect(4)">Playfield DB</a></li>
</ul>

JavaScriptをHTML文書からではなく、XSLTスタイル・シートから呼び出すことはできるのでしょうか。XSLT拡張機能を使用すれば可能です。残念ながら、XSLT拡張機能はXSLT 1.0では完全に標準化されていません。XSLT 1.1では、このサポートが改善される予定です。

ヒント5: スタイル・シート作成の自動化

このヒントは、これまで挙げた5つのヒントの中で最も大掛かりなものです。

作成しなければならないスタイル・シートがあまりにも多い場合、1つのスタイル・シートから多数のスタイル・シートを作成できるようにすると便利かもしれません。これは、考えるほど難しいことではありませんし、各国語版を備えたWebサイトや、細かな違いしかない多数のページを備えたサイトでは特に便利です。

この点を、リスト9のstart.xsl スタイル・シートで例証します。一見、これは典型的なスタイル・シートのように見えます。よく見ると、<link><para> などの特殊な要素がHTML要素の代わりに使用されていることに気付くでしょう(上記の特殊な要素は、HTMLでは<a><p> に相当します)。さらに、<xsl:output> 要素に method 属性がありません。

リスト9. start.xsl (一般的なXSLTスタイル・シート)
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0">
<xsl:output/>
<xsl:template match="/">
   <page title="XSLT Through Generation">
      <xsl:for-each select="products/product">
         <para>
            <link href="{@href}"><xsl:value-of select="name"/></link>
         </para>
      </xsl:for-each>
   </page>
</xsl:template>
</xsl:stylesheet>

秘けつは、start.xsl をさらに独特なスタイル・シートにするために、リスト10に示す、generate_html.xsl という別のスタイル・シートを使用することです。

リスト10. generate_html.xsl (start.xslをHTML用にするスタイル・シート)
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0">
<xsl:template match="xsl:output">
  <xsl:copy>
     <xsl:attribute name="method">html</xsl:attribute>
  </xsl:copy>
</xsl:template>
<xsl:template match="page">
   <html>
      <head><title><xsl:value-of select="@title"/></title></head>
      <body>
         <h1><xsl:value-of select="@title"/></h1>
         <xsl:apply-templates/>
      </body>
   </html>
</xsl:template>
<xsl:template match="para">
   <p><xsl:apply-templates/></p>
</xsl:template>
<xsl:template match="link">
   <a href="{@href}"><xsl:apply-templates/></a>
</xsl:template>
<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>
</xsl:stylesheet>

generate_html.xsl スタイル・シートはstart.xsl をXML文書と見なし、これを別のXML文書に変換します。start.xsl自体がXSLTスタイル・シートであることは、generate_html.xslにとっては関係のないことです。たとえば、次の規則によって<link> 要素が<a> になります。

<xsl:template match="link">
   <a href="{@href}"><xsl:apply-templates/></a>
</xsl:template>

次のコードは、method 属性を追加することにより、<xsl:output> 要素を修正します。

<xsl:template match="xsl:output">
  <xsl:copy>
     <xsl:attribute name="method">html</xsl:attribute>
  </xsl:copy>
</xsl:template>

スタイル・シートの他の要素 (<xsl:template><xsl:for-each>、および他のXSLT命令) は、未修正のままコピーされます。

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

generate_html.xslをstart.xslに適用すると、スタイル・シートgenerated.xsl が作成されます。これは、start.xsl ではなく、generated.xslであり、HTML文書を作成するためのものです。

このヒントは、犬が自分の尾を追いかけて走るようなものではありません。XSLTの論理を最大限に推し進めることによって得られる益を示す例となっています。確かに、XSLTはXML文書を他のXML文書に変換する点で優れているのですから、XSLTスタイル・シート自体を変換する点でも理想的であるのは当然のことです。

リスト11. generated.xsl (start.xslをHTML向けに特殊化した例)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
  <html><head><title>XSLT Through Generation</title></head>
  <body><h1>XSLT Through Generation</h1>
     <xsl:for-each select="products/product">
        <p>
           <a href="{@href}"><xsl:value-of select="name"/></a>
        </p>
     </xsl:for-each>
  </body></html>
</xsl:template>
</xsl:stylesheet>

自分のWebサイトで多数のスタイル・シートを作成しなければならない場合、このヒントを適用したいと思われることでしょう。たとえば、generate_html.xslgenerate_wml.xsl で置き換えれば、自動的にstart.xsl をWML向けにすることができます (WMLはワイヤレス・スマートフォン向けのマークアップ言語)。

リスト12. generate_wml.xsl (start.xslをWML向けにする)
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0">
<xsl:template match="xsl:output">
<xsl:copy>
  <xsl:attribute name="method">xml</xsl:attribute>
  <xsl:attribute name="doctype-public">-//WAPFORUM//DTD WML 1.1//EN</xsl:attribute>
  <xsl:attribute name="doctype-system">http://www.wapforum.org/DTD/wml_1.1.xml
</xsl:attribute>
</xsl:copy>
</xsl:template>
<xsl:template match="page">
   <wml>
      <card title="{@title}">
         <xsl:apply-templates/>
      </card>
   </wml>
</xsl:template>
<xsl:template match="para">
   <p><xsl:apply-templates/></p>
</xsl:template>
<xsl:template match="link">
   <anchor><go href="{@href}"/><xsl:apply-templates/></anchor>
</xsl:template>
<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>
</xsl:stylesheet>

この話の教訓は、大規模なWebサイトで自動化を最大限に推し進めようとするならば、XML文書からHTML文書を自動生成するだけでは不十分であり、XSLTスタイル・シートも自動生成すべきだということです。


ダウンロード可能なリソース


関連トピック

  • Xalan XSLTプロセッサーは、オープン・ソースとして配布されています。Java版とC++ 版があります。
  • 標準規格に準拠したスタイル・シートをInternet Explorerと併用するには、MSXML 3.0 へのアップグレードが必要です。
  • XSLT 1.0 は、本稿執筆時点でXSLTの公式の最新版です。
  • XSLT 1.1 は現在開発中です。必要な改善がいくつか加えられています。
  • W3CのXSLTページには、多数のXSLT参考文献が紹介されています。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=243381
ArticleTitle=XSLTのコーディングを改善する5つの方法
publish-date=01012001