tohokuaikiのチラシの裏

技術的ネタとか。

XSLの変数とそのスコープについて

XSLの変数には2つある。paramとvariable

variableは変数なんだけど、どっちかというと定数に近い概念で一度設定すると上書きができない。

variable
  1. 上書きができない
  2. xsl:stylesheetの要素に記述することができて、その場合はグローバル変数になる
param
  1. テンプレート呼び出し時に関数のようにデフォルト値宣言できる
<xsl:apply-templates select="foo">
     <xsl:with-param name="counter" select="$count+1"/>
</foo>
<xsl:templates match="foo">
     <xsl:param name="counter"/><!-- この時点で↑から呼び出された場合は$counter = $count+1が入っている -->
     <xsl:value-of select="$counter"/>
</xsl:templates>

種類

変数には、2つのものがある。1つはリテラル(普通の文字列)で、もう一つはXML要素セットである。

リテラル
<xsl:variable name="foo" select="'bar'"/>

リテラルの場合は、シングルクォートで囲う。

要素セット

要素として変数を格納することができる。
その結果がリテラルとして評価されることもある。たとえば属性を選んだ場合だ。

<root documenttype="html">
    <PLANET>
        <NAME>Mercury</NAME>
    </PLANET>
    <PLANET>
        <NAME>Venus</NAME>
    </PLANET>
    <PLANET class="earth">
        <NAME>Earth</NAME>
    </PLANET>
</root>

というXMLに対して、

<xsl:variable name="foo" select="/root/@documenttype"/>

とした場合は、リテラル'html'が$fooに入る。

しかし、

<xsl:variable name="foo" select="/root/PLANET"/>

とした場合、3つの要素が$fooに代入される。その場合はで展開して使用する。

使い方

単純に

<xsl:value-of select="$foo"/>

リテラルにアクセスできる。

要素集合へのアクセスは展開して使う。

<xsl:for-each select="$foo">
    <xsl:apply-templates select="."/>
</xsl:for-each>

更に、おまけ。elementの引数として使いたい場合などは中カッコで囲む。

<xsl:element name="{$topictype}">foo</xsl:element>

何でもかんでも中カッコで囲うとエラーが出る。たとえば、value-ofで変数を使用する際には中カッコで囲んではいけない

変数のスコープ

ローカル変数は、その宣言の親要素にはスコープが及ばない。
これは結構難解な問題を与えてて、普通のプログラミングのように上の方で変数をセットする際に、if分岐がほとんど使えないことを意味する。

たとえば、こういうことをしても、意味が無い。

<xsl:template match="/root">
  <xsl:if test="$foo='bar'">
     <xsl:variable name="FOO" select="'VAR'"/>
  </xsl:if>

なぜなら、$FOOはそのIFスコープ内でしか有効でないからだ。
更にいうと、xsl:ifは内でしか使えないから他のテンプレートへの変数波及はしない。

そこで、こういう感じにする

<xsl:stylesheet ...>
	<xsl:variable name="foo" select="'bar'"/>
	<xsl:variable name="FOO">
		<xsl:if test="$foo='bar'">BAR</xsl:if>
	</xsl:variable>