ルックアップ・テーブルは、プログラミングによって結果を出そうとすると非常に効率が悪かったり、それが不可能だったりする場合に、素早く結果を見つけ出すためによく使われる技法です。ルックアップ・テーブルは、キーを値に対応付けるためのマッピングで構成されます。ルックアップ・テーブルは、XSLTにおいても、他の言語の場合と同じように便利なものになり得ます。その理由は特に、XSLTはXMLツリーを変換することに高度に特化した言語なので、ストリングや数値の操作をあまり得意としていないところにあります。
本稿では、XSLTにおいてルックアップ・テーブルを使用する方法と、ルックアップ・テーブルをスタイル・シート内に使い易く収容する方法を紹介します。
さて、リスト1に示す、宛名ラベルを表すソース文書について考えてみましょう。
リスト1: 宛名ラベル
<?xml version="1.0"?>
<labels>
<label>
<name>Thomas Eliot</name>
<address>
<street>3 Prufrock Lane</street>
<city>Hartford</city>
<state>CT</state>
</address>
</label>
<label>
<name>Ezra Pound</name>
<address>
<street>45 Usura Place</street>
<city>Hailey</city>
<state>ID</state>
</address>
</label>
<label>
<name>William Williams</name>
<address>
<street>100 Wheelbarrow Blvd</street>
<city>Patterson</city>
<state>NJ</state>
</address>
</label>
</labels>
|
. . . そして、この文書を基にして、リスト2のような、氏名と、その人が住んでいる州を要約したリストを生成することにします。
リスト2: 氏名と居住している州の要約
Thomas Eliot of Connecticut
Ezra Pound of Idaho
William Williams of New Jersey
|
ここで必要になるのは、州名の省略形 (たとえば "ID") を正式な名称(たとえば "Idaho") に対応付ける手段です。もちろん、ソース文書の作成者が最初から州の正式な名前を使ってくれていれば、何の問題もありません。しかし、ソース文書の形式をいつも自分で管理できるとは限らないわけです。
1つのソリューションは、ルックアップ・テーブル文書を作成するという方法です。その場合、ルックアップ・テーブルはリスト3のような簡単なものになります。
リスト3: ルックアップ・テーブル文書
<?xml version="1.0"?>
<states>
<state><abbr>CO</abbr><name>Colorado</name></state>
<!-- etc. -->
<state><abbr>CT</abbr><name>Connecticut</name></state>
<!-- etc. -->
<state><abbr>ID</abbr><name>Idaho</name></state>
<!-- etc. -->
<state><abbr>NJ</abbr><name>New Jersey</name></state>
<!-- etc. -->
</states>
|
この文書にはすべての州をリストしていませんが、文書の作り方はおわかりいただけるでしょう。リスト4に、states.xmlというルックアップ・テーブルを使って必要な結果を生成するスタイル・シートを掲載します。このスタイル・シートには、それぞれの変換ブロックごとにコメントとして英文字を入れておきました。これは、リストの後の補足説明に対応しています。
リスト4: 変換用のスタイル・シート (注釈入り)
<?xml version="1.0"?>
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<!-- A -->
<xsl:key name="state-lookup" match="state" use="abbr"/>
<!-- B -->
<xsl:variable name="states-top" select="document('states.xml')/states"/>
<xsl:template match="label">
<xsl:value-of select="name"/>
<xsl:text> of </xsl:text>
<!-- C -->
<xsl:apply-templates select="$states-top">
<xsl:with-param name="curr-label" select="."/>
</xsl:apply-templates>
</xsl:template>
<!-- D -->
<xsl:template match="states">
<xsl:param name="curr-label"/>
<xsl:value-of select="key('state-lookup', $curr-label/address/state)/name"/>
</xsl:template>
</xsl:transform>
|
A. 最初に、スタイル・シート内でルックアップ・テーブルを表すキーを定義します。XSLTのキーでは、match 属性によって選択されるソース文書内のノードについて、名前付きインデックスが作成されます。キーは、use 属性から算出されます。ここで重要なのは、(document() 関数を使って) ロードされる各ソース文書ごとに別々のインデックスが用意されるという点です。key() 関数を使ってキーを照会すると、コンテキスト・ノードを含むソース文書のインデックスが使用されます。
B. この変数には、states.xml文書の文書エレメント(states) が設定されます。この場合、その文書がロードされる時点で、その文書のインデックスの生成がトリガーされます。ここで、元のソース文書 (宛名ラベルの文書) には、state-lookup というキーの定義にマッチするノードがないので、そのインデックスは空であることに注意してください。
C. この時点までで、宛名ラベルにある氏名と、"of" というテキストの生成が終わります。次に必要なのは、ルックアップ・テーブルを使って州の正式な名前を生成することです。それには、次のように記述すればよいと思うことでしょう。
<xsl:value-of select="key('state-lookup', address/state)/name"/>
|
しかし、すぐに思いつくこの書き方では、何も生成されません。なぜなら、すでに述べたとおり、キーが呼び出された時点で検索されるのは、コンテキスト・ノードを含む文書のインデックスだからです。この時点で、コンテキストは元のソース文書のlabelエレメントです。そして、state-lookup インデックスは、まだ空なのです。したがって、コンテキストをstates.xml文書に移す必要があります。その文書のインデックスに、必要な項目が入っているからです。
そのための最も直接的な方法は、xsl:apply-templates を使ってstates.xml文書内のノードを選択することです。それにはstates-top 変数が打ってつけだと思えるでしょう。しかし、現在のコンテキスト・ノードの位置も覚えておく必要があることに注意してください。そうしないと、現在処理中の人が住んでいる州の省略形を調べられないからです。この問題を解決するには、現在のノードをパラメーターとして次のテンプレートに引き渡すことができます。
D. ここまでくれば、キーを呼び出して必要な出力を生成するのは簡単です。パラメーターとして渡されたlabelノードのaddress 子エレメントのstate 子エレメントのストリング値を使ってルックアップ・テーブルを調べればよいのです。キーのルックアップの結果は、states.xml文書内の1つのstate エレメントを含んだノード・セット1つになります。したがって、最後に、そのエレメントのname 子エレメントを選択すれば、その中に、探している正式な州名が入っています。
このスタイル・シートを実際に実行してみてください。以前にお見せしたのと同じ、次のような結果が得られるはずです。 リスト5: キーを呼び出す
[uogbuji@borgia code]$ 4xslt labels.xml state-summary.xslt Thomas Eliot of Connecticut Ezra Pound of Idaho William Williams of New Jersey [uogbuji@borgia code]$ |
ルックアップ・テーブルを変換スタイル・シートそのものに入れる
本稿で考えている課題は、ルックアップ・テーブルを保持することによって解決できました。しかし、XSLT変換スタイル・シートとルックアップ・テーブル文書という2つの文書をいつも提供しなければならないというのは不便かもしれません。幸いなことに、XMLの名前空間を利用すれば、ルックアップ・テーブルをスタイル・シートそのものの中にごく簡単に組み込むことができます。
リスト6: スタイル・シートの中のルックアップ・テーブル
<?xml version="1.0"?>
<!-- E -->
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:s="http://states.data"
version="1.0">
<xsl:output method="text"/>
<!-- F -->
<xsl:key name="state-lookup" match="s:state" use="s:abbr"/>
<!-- G -->
<xsl:variable name="states-top" select="document('')/*/s:states"/>
<xsl:template match="label">
<xsl:value-of select="name"/>
<xsl:text> of </xsl:text>
<xsl:apply-templates select="$states-top">
<xsl:with-param name="curr-label" select="."/>
</xsl:apply-templates>
</xsl:template>
<!-- H -->
<xsl:template match="s:states">
<xsl:param name="curr-label"/>
<xsl:value-of select="key('state-lookup', $curr-label/address/state)/s:name"/>
</xsl:template>
<!-- I -->
<s:states>
<s:state><s:abbr>CO</s:abbr><s:name>Colorado</s:name></s:state>
<s:state><s:abbr>CT</s:abbr><s:name>Connecticut</s:name></s:state>
<s:state><s:abbr>ID</s:abbr><s:name>Idaho</s:name></s:state>
<s:state><s:abbr>NJ</s:abbr><s:name>New Jersey</s:name></s:state>
</s:states>
</xsl:transform>
|
E. 組み込みルックアップ・テーブルのための名前空間を取り分けておく必要があります。そうしないと、XSLTがルックアップ・テーブルのエレメントを読み込んだ時点で、エラーが通知されます。XSLTプロセッサーは、xsl以外の名前空間に属する最上位エレメントを無視しなければならないのですが、この技法ではその事実をうまく利用するわけです。
F. ここでは、ルックアップ・テーブルのエレメントに対して名前空間の接頭部を使用する必要があります。
G. ここでは、document() 関数に空ストリングを渡すと、ソース文書としてスタイル・シートそのものが返される、という特別な規則を利用しています。ここでも、ルックアップ・テーブルのエレメントに対して名前空間の接頭部を使用する必要があります。このコードによって、スタイル・シートそのものがロードされて、組み込みルックアップ・エレメントのインデックスが生成されます。また、この技法の場合は、s:states エレメントが文書エレメントではなく、xsl:transform の子エレメントであることにも注意してください。したがって、変数のselect属性の内容に、その違いを反映させる必要があります。
H. Fと同じコメントが当てはまります。
I. これが、組み込みルックアップ・テーブルです。前のルックアップ・テーブルとの違いは、すべてのエレメントが名前空間に属していることと、s:states が文書エレメントではないことだけです。
2番目のソリューションは、便利で美しい技法です。必要なのはスタイル・シートだけで、ただ値を検索するだけのために別個の文書を取り扱う手間が省けるのです。
この技法は、さまざまな方法で活用できます。ルックアップ・テーブルは、本稿で紹介した例のほかにも、"1/1/2001" といった日付を "January 1, 2001" といった形式に変換するためにも利用できるでしょう。
もちろん、ルックアップ・テーブルを別個の文書として用意したほうが便利な場合もあります。たとえば、XML文書内の通貨のデータを、XML対応のデータベースへの照会結果から取得したルックアップ・テーブルを利用して、米ドルから英ポンドに変換するアプリケーションの場合などです。
本稿で紹介した技法が読者の役に立つこと、そして、この技法に含まれているXSLTを活用するためのヒントから読者が何か1つでも学び取れることを期待しています。
-
W3CのXSL のページには、XSLTの仕様、チュートリアル、各種の関連記事、XSLTのインプリメンテーションなど、XSLTに関連した多くの有用なリソースが掲載されています。
- 本稿のサンプルで使用したXSLTプロセッサー4XSLTは、4Suite から入手できます。
