Jintrick.netagenda2003年11月アーカイブ → 2003年11月07日

XSLTをテンプレートとして使用する

一般的な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> &nbsp;
#else
$tool.menuitem() &nbsp;
#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インスタンス)に与えるパラメタとして扱おうと考えました。例:

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>
XSLTテンプレートに与えるXMLパラメタの例(実質的にソース文書)

<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>
生成されるHTML
<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メソッドが無かったり、微妙に違うのが厄介なのであまり使いたくありませんでしたが、滅多に手を加える部分ではないので目をつむりました。


webmaster@jintrick.net
公開: 2003年11月07日
カテゴリ: DOM ,Python ,XSLT