XSLTフィルターでつくるXHTML文書

[1] XSLTフィルターとは

xalan や xsltproc のようなXSLTプロセッサーでは、コマンドをパイプ( | )でつなぐことで、一連のXSLT変換処理を一度に行うことができます。


(xsltproc の場合)
$ xsltproc f1.xsl src.xml | xsltproc f2.xsl - | xsltproc f3.xsl

(xalanの場合)
$ xalan src.xml f1.xsl | xalan - f2.xsl | xalan - f3.xsl

ここで src.xml はソースとなるXML文書を表します。 f1.xsl ... f3.xsl はXSLTスタイルシートです。これらパイプ処理中のスタイルシートを以下では「XSLTフィルター」または単に「フィルター」と呼ぶことにします。

上の例では、結果はどちらも標準出力に出されます。ファイル rst.xml に出力したい場合には、コマンドラインの末尾に


> rst.xml

を付け足します。

[2] XSLTフィルターの応用例

XHTML文書をインターネットで公開する際、ソース文書にいろいろな情報を追加したりソース文書の内容を修正したりする必要の出てくることがあります。 その仕事が、XSLTフィルターを使えば簡単にできます。

以下ではこういった目的のためのXSLTフィルターの作成法を有用な例を用いて紹介します。

[3] ソース文書を用意する

その前に例で使用するソースXHTML文書 hello.src.html がどんなものであるかを書いておきます。


[hello.src.html]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html SYSTEM "/cygwin/home/hiro/work/dtd/xhtml1-strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Hello, World</title>
  </head>
  <body>
    <h1>Hello, World!</h1>
    <p>Please visit my 
<a href="http://www15.plala.or.jp/h-kihara-home/index.html">home</a>.</p>
    <table>
      <!-- one of (tbody tr) -->
      <tr>
        <!-- one of (td th) -->
        <th>Name</th>
        <td>H.Kihara</td>
      </tr>
      <tr>
        <!-- one of (td th) -->
        <th>Birth Place</th>
        <td>Japan</td>
      </tr>
    </table>
  </body>
</html>

上の validated なXHTML文書は Emacs(Meadow)+PSGMLで作成しました。 /cygwin/home/hiro/work/dtd/xhtml1-strict.dtd は筆者のパソコンにおける xhtml1-strict.dtd へのパスです。 筆者はWindowsパソコンで cygwin というUNIX的環境を用いています。

文書は utf-8 でエンコーディングされているものと仮定します。

[4] フィルターの基礎

すべてのフィルターに共通の処理を一個のスタイルシートに書いておけば、あとは個々のフィルターがそれをインポートすることで、その共通の処理を行うことができます。その共通の処理を行うフィルターを基礎フィルターと呼ぶことにします。

基礎フィルターとして次のXSLTスタイルシート xhtml-filter-base.xsl を用意しておくと便利です。


[xhtml-filter-base.xsl]
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:h="http://www.w3.org/1999/xhtml"
  xmlns="http://www.w3.org/1999/xhtml"
  exclude-result-prefixes="h"
  version="1.0">

  <xsl:output method="xml" indent="yes"
    omit-xml-declaration="no" encoding="utf-8"
    doctype-system="/cygwin/home/hiro/work/dtd/xhtml1-strict.dtd"/>

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



  <!-- Omit attributes with default values -->

  <xsl:template match="@shape[ string() = 'rect' ]">
  </xsl:template><!-- "a,area" -->

  <xsl:template match="@valuetype[ string() = 'data' ]">
  </xsl:template><!-- "param" -->

  <xsl:template match="@method[ string() = 'get' ]">
  </xsl:template><!-- "form" -->

  <xsl:template match="@enctype[ string() =
                       'application/x-www-form-urlencoded' ]">
  </xsl:template><!-- "form" -->

  <xsl:template match="@type[ name(parent::*) = 'input' and
                       string() = 'text']">
  </xsl:template><!-- only "input" -->

  <xsl:template match="@type[ name(parent::*) = 'button' and
                       string() = 'submit']">
  </xsl:template><!-- only "button" -->

  <xsl:template match="@span[ string() = '1']">
  </xsl:template><!-- "colgroup,col" -->

  <xsl:template match="@rowspan[ string() = '1' ]">
  </xsl:template><!-- "th,td"-->

  <xsl:template match="@colspan[ string() = '1' ]">
  </xsl:template><!-- "th,td"-->

</xsl:stylesheet>

xhtml-filter-base.xsl が単独でやっていることはソースの恒等変換です。 すなわち


$ xalan hello.src.html xhtml-filter-base.xsl > hello.html

または

$ xsltproc xhtml-filter-base.xsl hello.src.html > hello.html

とした場合、hello.src.html と hello.html は“同じ”XHTMLファイルになっています。 テキストファイルとして見たら正確には恒等変換になっていませんが、 XHTML では本質的に同じです。違いは省略していいスペースが省略されていたり、 大文字小文字を区別しなくていい英文字が強制的に小文字に変えられていたりといった程度の違い。

※ ただ xalan の変換は本質的に同じといってしまっていいか気になる点はある。 この点では xsltproc のほうがソースに忠実な結果を出してくるようである。 気になるならば二つを使った実験を沢山やって比較してみられるとよい (2011.10.31記)。

[5] フィルターのテンプレート

上の xhtml-filter-base.xsl をインポートする個々のXSLTフィルターは次のような形をとります。フィルターのテンプレートという意味で xhtml-filter-template.xsl という名前を付けます。


[xhtml-filter-template.xsl]
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:h="http://www.w3.org/1999/xhtml"
  xmlns="http://www.w3.org/1999/xhtml"
  exclude-result-prefixes="h"
  version="1.0">

  <xsl:import href="xhtml-filter-base.xsl"/>

<!-- Do something -->

</xsl:stylesheet>

個々のXSLTフィルターは上の <!-- Do something --> 以下にコードを書くことで作成していきます。

以下、XSLTフィルターの例を2,3つ紹介しましょう。

[6] 例1(フッターを付けるフィルター)

ソースの hello.src.html にはアドレス情報を含むフッターが欠けています。 ここでXHTMLのフッターとはたとえばこんな部分を言います。


    <hr/>
    <p>Copyright &#169; 2004 <a href="mailto:user@domain">H.Kihara</a> All Rights Reserved.</p>
    <address><a href="mailto:user@domain">Admin</a></address>

フッターは複数のXHTMLに共通にしたいので、個々のソースに書き込むよりも、XSLTで自動生成させたほうが合理的です。 フッターを作成するフィルターを上記のテンプレートを元に作成してみましょう。

フッターを付けるフィルター xhtml-add-footer.xsl は下のようになります。


[xhtml-add-footer.xsl]
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:h="http://www.w3.org/1999/xhtml"
  xmlns="http://www.w3.org/1999/xhtml"
  exclude-result-prefixes="h"
  version="1.0">

  <xsl:import href="xhtml-filter-base.xsl"/>

  <xsl:template match="h:body">
    <body>
      <xsl:apply-templates select="@*|node()"/>
      <xsl:call-template name="footer"/>
    </body>
  </xsl:template>

  <xsl:template name="footer">
    <hr/>
    <p>Copyright &#169; 2004 <a href="mailto:user@domain">H.Kihara</a> All Rights Reserved.</p>
    <address><a href="mailto:user@domain">Admin</a></address>
  </xsl:template>

</xsl:stylesheet>

[7] xslt変換してみる

ソース hello.src.html に上のスタイル xhtml-add-footer.xsl を適用してみます。


$ xsltproc xhtml-add-footer.xsl hello.src.html

すると標準出力から出てくる結果は下のようになります。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html SYSTEM "/cygwin/home/hiro/work/dtd/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Hello, World</title>
  </head>
  <body>
    <h1>Hello, World!</h1>
    <p>Please visit my 
<a href="http://www15.plala.or.jp/h-kihara-home/index.html">home</a>.</p>
    <table>
<!-- one of (tbody tr) -->
      <tr>
<!-- one of (td th) -->
        <th>Name</th>
        <td>H.Kihara</td>
      </tr>
      <tr>
<!-- one of (td th) -->
        <th>Birth Place</th>
        <td>Japan</td>
      </tr>
    </table>
    <hr/>
    <p>Copyright © <a href="mailto:user@domain">H.Kihara</a> All Rights Reserved.</p>
    <address>
      <a href="mailto:user@domain">Admin</a>
    </address>
  </body>
</html>

なお上の例ではXSLTプロセッサに xsltproc を用いましたが、xalan を用いても結果は本質的に同じです。

[8] 例2( 公開識別子を付けるフィルター)

ソース に 上記の xhtml-add-footer.xsl を適用しただけでは、まだWEBで公開することはできません。上の第二行のDOCTYPE宣言


<!DOCTYPE html SYSTEM "/cygwin/home/hiro/work/dtd/xhtml1-strict.dtd">

ではローカルなDTDが指定されているからです。ここは


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

に書き換えなければなりません。 この書き換えはXSLTフィルターで処理することができます。

またソース中に公開する必要のないコメント文が入っているのも気にかかります。これらを削除するのもフィルターの仕事です。

これらの問題を解決し、最終的にWEBサーバで公開できるXHTMLを生成するフィルター xhtml-for-public.xsl は次のようなものになります。


[xhtml-for-public.xsl]
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:h="http://www.w3.org/1999/xhtml"
  xmlns="http://www.w3.org/1999/xhtml"
  exclude-result-prefixes="h"
  version="1.0">

  <xsl:import href="xhtml-filter-base.xsl"/>

  <xsl:output method="xml" indent="yes"
    omit-xml-declaration="yes"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>

  <!-- delete comments -->
  <xsl:template match="comment()">
  </xsl:template>

</xsl:stylesheet>

[9] xslt変換してみる

ソース hello.src.html に xhtml-add-footer.xsl と xslt-for-public.xsl をこの順序で適用してみましょう。


$ xsltproc xhtml-add-footer.xsl hello.src.html | xsltproc xhtml-for-public.xsl -

標準出力から出てくる結果は次のようになります。


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Hello, World</title>
  </head>
  <body>
    <h1>Hello, World!</h1>
    <p>Please visit my 
<a href="http://www15.plala.or.jp/h-kihara-home/index.html">home</a>.</p>
    <table>
      <tr>
        <th>Name</th>
        <td>H.Kihara</td>
      </tr>
      <tr>
        <th>Birth Place</th>
        <td>Japan</td>
      </tr>
    </table>
    <hr />
    <p>Copyright © 2004 <a href="mailto:user@domain">H.Kihara</a> All Rights Reserved.</p>
    <address>
      <a href="mailto:user@domain">Admin</a>
    </address>
  </body>
</html>

上の結果を hello.html として保存します。

hello.htmlThe W3C Markup Validation Service で Valid なXHTML文書になっているか検証してみましょう。

"This Page Is Valid XHTML 1.0 Strict!” と表示されるはずです。これは hello.html が valid なXHTML1.0文書であることを意味します。