一般的なContent Management Systemにおいて、書式の編集にはしばしば独自のテンプレートが使用されます。このテンプレートをXSLTで代用する方法について検討します。
一般的なテンプレートとして、PyDS(Python Desktop Server)のそれを見てみますと、例えばこのようになっています:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>$request.getDesktopTitle()</title>
<link rel="stylesheet"
href="$macros.joinUrl($current.pathprefix(),'/static/pyds.css')" type="text/css">
<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
</head>
<body>
<h1>$request.getDesktopTitle()</h1>
<table width="100%" border=0>
<tr>
<td colspan=2 width="90%">
$macros.onlineStatus($macros.joinUrl($current.pathprefix(), $request.request.uri))
#for $tool in $tools.getFirstToolbar()
#if $name == $tool.name
<b>$tool.menuitem()</b>
#else
$tool.menuitem()
#end if
#end for
</td>
</tr>
#if $withcalendar
<tr><td colspan=2><hr></td></tr>
#else
<tr><td><hr></td></tr>
#end if
<tr>
#if $withcalendar
<td width="80%" valign="top">
#else
<td width="100%" valign="top">
#end if
#if $errmsg
<p class="errormsg">$errmsg
#end if
#if $infomsg
<p class="infomsg">$infomsg
#end if
$body
</td>
#if $withcalendar
<td width="20%" valign="top">
<div class="calendarbox">$request.renderCalendar(category=$getVar('category',''))</div>
$current.searchbox()
#for $tool in $tools.getSecondToolbar()
#set nugget = $tool.nugget()
#if $nugget
<p class="whiteboxsmall">$nugget
#end if
#end for
#for $tool in $tools.getThirdToolbar()
#set nugget = $tool.nugget()
#if $nugget
<p class="whiteboxsmall">$nugget
#end if
#end for
</td>
#end if
</tr>
</table>
</body>
</html>
つまり、ただ単にHTMLの中に変数を埋め込むというだけでは、甚だ貧弱で静的なテンプレートにしかならない為、このように条件分岐やループを表現するための、独自の構文を考えねばならないのです。そのような手間を省くため、このようなテンプレートの書式として、既知であり、プロセッサも既に存在しているXSLTを選んでみようというわけです。
しかしXSLTはテンプレートを定義することができますが、基本的には変換元の文書(ソース文書)を変換する為の言語です。そこで、「変換元のXML文書」を文書としてではなく、テンプレート(XSLTインスタンス)に与えるパラメタとして扱おうと考えました。例:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:params="http://purl.org/jitrick/Perosnal/2003/11/06/params/"
exclude-result-prefixes="rdf params"
>
<!--ディレクトリのインデックスを作成するテンプレート-->
<xsl:output
method="html"
indent="yes"
encoding="UTF-8"
/>
<xsl:template match="/">
<xsl:variable name="list" select="/descendant::params:list" />
<xsl:variable name="title" select="/descendant::params:title" />
<title><xsl:value-of select="$title" /></title>
<h1><xsl:value-of select="$title" /></h1>
<ul>
<xsl:for-each select="$list/child::*/child::rdf:li">
<li>
<a href="{self::*}"><xsl:value-of select="string(self::*)" /></a>
</li>
</xsl:for-each>
</ul>
</xsl:template>
</xsl:stylesheet>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/jitrick/Perosnal/2003/11/06/params/">
<param>
<title>Index of "/"</title>
<list>
<rdf:Bag>
<rdf:li>index.html</rdf:li>
<rdf:li>about.html</rdf:li>
<rdf:li>style.css</rdf:li>
</rdf:Bag>
</list>
</param>
</rdf:RDF>
<title>Index of "/"</title>
<h1>Index of "/"</h1>
<ul>
<li><a href="index.html">index.html</a></li>
<li><a href="about.html">about.html</a></li>
<li><a href="style.css">style.css</a></li>
</ul>
XMLパラメタの書式としてRDF/XMLを使用していますが、必要性はありません。これはXSLTプロセッサに渡し終えたら破棄してしまいます。
あるXSLTプロセッサを表すクラスを考えます。インスタンスにスタイルシートを与えて、変換メソッドの引数にソース文書を与えて変換を行うという一般的なものです(異なるモデルもあり得ますが、このように抽象化してあるのが一般的です)。
XSLTをテンプレート言語として利用する際の特殊な操作は、テンプレートに与えるパラメタをXML化し、それを変換メソッドの引数に与える、たったこれだけです。
ここで「ソース文書」というのは甚だ抽象的です。実際には、そのURIであったりパスであったり、文書を表すクラスのインスタンスであったり、または文書のDOMノード(DOMDocument)であったり、さらには具体的なstringであったりします。大抵のプロセッサは、実際のURIやパスを持ったソース文書の他に、メモリ上に存在するソース文書を変換するためのメソッドがあります。それを使用し、引数の型はそれに合わせます。そのようなメソッドが無い場合、そのプロセッサは捨ててしまいましょう。
2003年の夏ごろから、私はPythonというスクリプト言語を使い始めました。XSLTプロセッサは、4Suiteの提供するFt.Xml.Xslt.Processorを使用します。
このプロセッサではソース文書をDOMDocumentとして渡す(こともできる)仕様になっていますが、正確にはノードがNodeインターフェイスを継承し、非標準のアトリビュート、「rootNode」を持たなければなりません(どこにも書いていないけれど)。従って、標準的な実装では駄目で、4Suiteが提供する、例えば Ft.Xml.Domlette の DOMImplementationを使用しなければなりませんでした。
Domletteは、概ねDOM level2に準拠しているものの、NodeListにitemメソッドが無かったり、微妙に違うのが厄介なのであまり使いたくありませんでしたが、滅多に手を加える部分ではないので目をつむりました。