2006 年 9 月 27 日 23 時 57 分

XSL #3: XPath と組み込みテンプレート


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


XSLT では、XPath を使って元の XML 文書を処理する。
XPath では、XML 文書の内容を「ノード」に分解し、
「ノード」全てを 1 つの大きな階層構造として取り扱う。

XML の要素だけでなく、テキストや、コメント、
そして属性や処理命令なども全てノードとして扱われる。
ノードは階層構造となり、最上位のノードは、
仮想的なノードである「ルートノード」である。
XML の文書要素はルートノードの子供ノードとなる。

    <root>
        <p align="center">
          テキストも<em>ノード</em>として扱います。
        </p>
    </root>
    <?apply href="footer.xml" ?>

この XML 文書に当てはめてみよう。

まず、全てを含む「ルートノード」があり、
root 要素と apply 処理命令が子ノードである。
そして p 要素 は root 要素の子ノードである。

p 要素は 3 つの子ノードを持つ。
テキストノード「テキストも」と em 要素、
そして、テキストノード「として扱います。」だ。
em 要素はテキストノード「ノード」を子ノードとして持つ。

上記では親子関係も成り立つ。
つまり、em 要素は root 要素を親ノードに持つ。
ここまでは DOM と同じような考え方だ。

XPath で特徴的なのは、属性の扱い方だ。

上記の例では、p 要素には align 属性がある。
XPath では属性もノードとなるため、
align 属性は、p 要素を親ノードに持つ。
しかし、p 要素は align 属性を子ノードとしては持たない。

ルートノードから、子ノードの階層をたどる場合、
属性ノードはそこに含まれずに飛ばされるということだ。
簡単に言うと、子ノード全体に処理をするような場合でも、
属性ノードはそこに含まれないということである。

p 要素が align 属性の親ノードであることは間違いないが、
p 要素から見た align 属性は「子ノード」ではなく、
p 要素が持つ「属性」という別の階層に位置付くことになる。

さて、XLST を使った変換は、変換元の XML 文書を
XPath の階層モデルに当てはめ、
ノードに着目しながら変換処理を行う。

この着目されているノードのことを、
カレントノードや、現在のノードと呼ぶ。
XLST では、最初にルートノードをカレントノードとし、
カレントノードに対して以下の処理を行う。

1. カレントノードに一致するテンプレートを探す。
2. 複数一致した場合は、優先順位に沿って 1 つ選択する。
  一致するテンプレートが見つからない場合は、
  代わりに組み込みのテンプレートを使う。
3. テンプレートの内容に従って変換を行う。

XLST による変換は、基本的にこの処理だけである。
この一連の処理を「ノード変換」と呼ぶことにしよう。

昨日は、テンプレートを殆ど記述していなかったため、
XML 内のテキストが全て出力された。
これは、組み込みのテンプレートが機能したからである。

では、組み込みのテンプレートについて説明しておこう。
組み込みのテンプレートを明示した場合、以下のようになる。

    <xsl:template match="/|*">
        <xsl:apply-templates />
    </xsl:template>

    <xsl:template match="@*|text()">
        <xsl:value-of select="." />
    </xsl:template>

    <xsl:template match="processing-instruction()|comment()" />

最初のテンプレートは、
ルートノード(/)と、カレントノードの子ノードである
要素(*)全てに適用される。

内容は、<xsl:apply-templates /> だ。
これは、自分のもつ子ノード全て(孫は含まない)に対して、
それぞれをカレントノードとしてノード変換を行う。

2 つ目のテンプレートは、カレントノードの子ノードである
属性ノード全て(@*)とテキストノード全て(text())に適用される。

内容は <xsl:value-of select="." /> だ。
value-of 要素は、指定した値を文字列化する。
値は select 属性で XPath 式を使って指定する。

. は、カレントノードを表すので、
属性やテキストノードをノード変換する場合、
自分自身を文字列化したものが出力されることになる。
なお、属性を文字列化した場合はその属性値、
テキストノードを文字列化した場合はテキストそのものとなる。

最後のテンプレートは、カレントノードの子ノードである
処理命令とコメントに適用される。
(processing-instruction(), comment())
このテンプレートの内容は空なので、
何も生成しない上に何の処理も行わないことになる。

以上から考えると、既定のテンプレートの場合、
ルートノードや要素が処理される際は、
自動的にその子ノードも全てノード変換されるため、
結果的にツリーのノード全てがノード変換の対象となる。

ただし、上にも書いたが属性は子ノードに含まれないため、
既定のテンプレートだけでは、
属性がカレントノードになることはない。
つまり、属性に関する変換テンプレートはあるのだが、
属性に対しては一切ノード変換が行われないことになる。

さて、要素のテンプレートには
<xsl:apply-templates /> しかないため、
子ノードの処理を続行する機能はあるのだが、
要素自身は何も出力しないことになる。

最終的に、テキストノードだけが、
自身のテキストを出力する以外には、
何も出力が行われないことになる。

昨日の例では、以下のようなテンプレートだけを定めた。

    <xsl:template match="/">
        <html>
            <head>
                <title>Test</title>
            </head>
            <body>
                <xsl:apply-templates />
            </body>
        </html>
    </xsl:template>

この場合、<body> と </body> の間に、
<xsl:apply-templates /> があるが、
これらには既定のテンプレートが適用されたため、
文字列だけが表示されるという結果となったのだ。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.