変数のデフォルト
はじまり
リンクのページにあるリンクバナーの指定に手を入れようとしたことから始まりました.リンクバナーはimgタグを使い,このように記述しています.
<a href="http://www.atmarkit.co.jp/"><img class="banner" src="banner/link_banner2.gif" height="31" width="88" alt="" /></a> <a href="http://lion_x.tripod.co.jp/index.html"><img class="banner" src="banner/lionban.gif" height="40" width="200" alt="" /></a> <a href="http://validator.w3.org/check?uri=http://www.dab.hi-ho.ne.jp/sasa/"><img class="banner" src="banner/valid-xhtml11.png" width="88" height="31" alt="Valid XHTML 1.1!" /></a>
imgタグの属性height,width,altはある程度決まった値になります.つまり,リンクバナーの多くには31×88ピクセルでaltは空文字を指定しています.そこで,linkタグとbannerタグを新設し,以下のような変換をxsltで行おうとしました.
- height属性は指定がなければ31,あればその値
- width属性は指定がなければ88,あればその値
- alt属性は指定がなければ空文字列,あればその値
<!-- 変換前 --> <link href="http://www.atmarkit.co.jp/"><banner src="banner/link_banner2.gif"/></link> <!-- width,height,alt指定なし --> <link href="http://lion_x.tripod.co.jp/index.html"><banner src="banner/lionban.gif" height="40" width="200"/></link> <!-- width,height指定あり.alt指定なし --> <link href="http://validator.w3.org/check?uri=http://www.dab.hi-ho.ne.jp/sasa/"><banner src="banner/valid-xhtml11.png" alt="Valid XHTML 1.1!"/></link> <!-- alt指定あり --> <!-- 変換後 --> <a href="http://www.atmarkit.co.jp/"><img class="banner" src="banner/link_banner2.gif" height="31" width="88" alt="" /></a> <a href="http://lion_x.tripod.co.jp/index.html"><img class="banner" src="banner/lionban.gif" height="40" width="200" alt="" /></a> <a href="http://validator.w3.org/check?uri=http://www.dab.hi-ho.ne.jp/sasa/"><img class="banner" src="banner/valid-xhtml11.png" width="88" height="31" alt="Valid XHTML 1.1!" /></a>
べたうち
とりあえず,height属性とwidth属性の有無により4通りの条件分岐を記述しました.alt属性は指定した値をそのまま使うので分岐の条件にはなりません.
<xsl:template match="link"> <a href="{@href}"><xsl:apply-templates/></a> </xsl:template> <xsl:template match="banner"> <xsl:choose> <xsl:when test="@height and @width"> <img class="banner" src="{@src}" height="{@height}" width="{@width}" alt="{@alt}"/> </xsl:when> <xsl:when test="@height"> <img class="banner" src="{@src}" height="{@height}" width="88" alt="{@alt}"/> </xsl:when> <xsl:when test="@width"> <img class="banner" src="{@src}" height="31" width="{@width}" alt="{@alt}"/> </xsl:when> <xsl:otherwise> <img class="banner" src="{@src}" height="31" width="88" alt="{@alt}"/> </xsl:otherwise> </xsl:choose> </xsl:template>
これであっさり実現できたのですが,あまり格好良くありません.
変数の使用
imgタグの記述は最後に一つにできるのではないかと考えました.C言語風に書くなら,こんな感じです.
int $height=88; if(@height){ $height=@height; } int $width=31; if(@width){ $width=@height; } <img class="banner" src="{@src}" height="{$height}" width="{$width}" alt="{@alt}"/>
これをこのままxsltに書いてみました.
<xsl:template match="link"> <a href="{@href}"><xsl:apply-templates/></a> </xsl:template> <xsl:template match="banner"> <xsl:variable name="height" select="31"/> <xsl:if test="@height"> <xsl:variable name="height" select="@height"/> </xsl:if> <xsl:variable name="width" select="88"/> <xsl:if test="@width"> <xsl:variable name="width" select="@width"/> </xsl:if> <img class="banner" src="{@src}" height="{$height}" width="{$width}" alt="{@alt}"/> </xsl:template>
結論から言うと,このスクリプトは意図した通りには動きませんでした.height属性とwidth属性にどんな値を設定しても,height=31 width=88となるのです.原因は変数のスコープにありました.
<xsl:template match="link"> <a href="{@href}"><xsl:apply-templates/></a> </xsl:template> <xsl:template match="banner"> <xsl:variable name="height" select="31"/> <xsl:if test="@height"> <xsl:variable name="height" select="@height"/> <!-- ここで作成した変数$heightはif要素の中のローカル変数.外側で定義した$heightとは別物で,if要素を抜けた時点で破棄 --> </xsl:if> <xsl:variable name="width" select="88"/> <xsl:if test="@width"> <xsl:variable name="width" select="@width"/> <!-- ここで作成した変数$widthはif要素の中のローカル変数.外側で定義した$widthとは別物で,if要素を抜けた時点で破棄 --> </xsl:if> <img class="banner" src="{@src}" height="{$height}" width="{$width}" alt="{@alt}"/> <!-- $height=31 width=88 --> </xsl:template>
C言語風に書くとこういうことなのです.
int $height=88; if(@height){ int $height=@height; //ここでローカル変数$heightを定義.ifのブロックを抜けた時点で破棄 } int $width=31; if(@width){ int $width=@height; //ここでローカル変数$widthを定義.ifのブロックを抜けた時点で破棄 } <img class="banner" src="{@src}" height="{$height}" width="{$width}" alt="{@alt}"/> //$height=31 width=88
xsltでは変数への代入ができないため,このやり方では実現できません.
結果ツリーフラグメント
xsltの変数は文字や数値以外にタグを代入できます.これを利用しました.
<xsl:template match="link"> <a href="{@href}"><xsl:apply-templates/></a> </xsl:template> <xsl:template match="banner"> <xsl:variable name="height"> <xsl:choose> <xsl:when test="@height"> <xsl:value-of select="@height"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="31"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:variable name="width"> <xsl:choose> <xsl:when test="@width"> <xsl:value-of select="@width"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="88"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <img class="banner" src="{@img}" height="{$height}" width="{$width}" alt="{@alt}"/> </xsl:template>
height属性とwidth属性への値の設定はうまくいきました.
テンプレートへの引数
<xsl:apply-templates>には引数を渡すことができ,引数はデフォルト値を指定することができます.このやり方でもうまくいきました.
<xsl:template match="link"> <a href="{@href}"> <xsl:choose> <xsl:when test="banner/@width and banner/@height"> <xsl:apply-templates select="banner"> <xsl:with-param name="width" select="banner/@width"/> <xsl:with-param name="height" select="banner/@height"/> </xsl:apply-templates> </xsl:when> <xsl:when test="banner/@width"> <xsl:apply-templates select="banner"> <xsl:with-param name="width" select="banner/@width"/> </xsl:apply-templates> </xsl:when> <xsl:when test="banner/@height"> <xsl:apply-templates select="banner"> <xsl:with-param name="height" select="banner/@height"/> </xsl:apply-templates> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="banner"/> </xsl:otherwise> </xsl:choose> </a> </xsl:template> <xsl:template match="banner"> <xsl:param name="width" select="88"/> <xsl:param name="height" select="31"/> <img class="banner" src="{@img}" width="{$width}" height="{$height}" alt="{@alt}"/> </xsl:template>
apply-templatesとchooseを入替えると,引数の値が次のテンプレートに渡せませんでした.引数にどんな値を指定しても88と31になりました.
<!-- これは引数に値が渡りません -->
<xsl:template match="link">
<a href="{@href}">
<xsl:apply-templates select="banner">
<xsl:choose>
<xsl:when test="banner/@width and banner/@height">
<xsl:with-param name="width" select="banner/@width"/>
<xsl:with-param name="height" select="banner/@height"/>
</xsl:when>
<xsl:when test="banner/@width">
<xsl:with-param name="width" select="banner/@width"/>
</xsl:when>
<xsl:when test="banner/@height">
<xsl:with-param name="height" select="banner/@height"/>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:apply-templates>
</a>
</xsl:template>
<xsl:template match="banner">
<xsl:param name="width" select="88"/>
<xsl:param name="height" select="31"/>
<img class="banner" src="{@img}" width="{$width}" height="{$height}" alt="{@alt}"/>
</xsl:template>
名前付きテンプレート
テンプレートへの引数同様,名前付きテンプレートへの引数もデフォルト値を持つことが可能です.これを利用しました.
<xsl:template match="link"> <a href="{@href}"><xsl:apply-templates/></a> </xsl:template> <xsl:template match="banner"> <xsl:choose> <xsl:when test="@height and @width"> <xsl:call-template name="banner"> <xsl:with-param name="height" select="@height"/> <xsl:with-param name="width" select="@width"/> </xsl:call-template> </xsl:when> <xsl:when test="@height"> <xsl:call-template name="banner"> <xsl:with-param name="height" select="@height"/> </xsl:call-template> </xsl:when> <xsl:when test="@width"> <xsl:call-template name="banner"> <xsl:with-param name="width" select="@width"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:call-template name="banner"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="banner"> <xsl:param name="width" select="88"/> <xsl:param name="height" select="31"/> <img class="banner" src="{@img}" height="{$height}" width="{$width}" alt="{@alt}"/> </xsl:template>
おしまい
結果ツリーフラグメント,テンプレート,名前付きテンプレートと作りましたが,結局最初に作ったべたうち版を使っています.
参考文献
Steven Holzner, Insede XSLT, New Riders Publishing, 2002
株式会社クイック訳,XSLT実践ガイド−XSLTスタイルシートによるXML文書の活用法−,株式会社アスキー,2002
株式会社ユニテック,改訂版 標準XML完全解説(下),株式会社技術評論社,2001
xsl:attributeを使う(2003.8.17追記)
このようなやりかたを教えていただきました.ありがとうございます.
imgタグの中に<xsl:attribute>で属性を設定し,その中でheightとwidthの設定を行います.わかりやすいやり方だと思います.
imgタグは
<img ... />
と書かなくてはという先入観があったようです.
<img ...> <xsl:attribute> </xsl:attribute> </img>
までは思い浮かびませんでした.
<xsl:template match="banner"> <img class="banner" src="{@src}" alt="{@alt}"> <xsl:attribute name="height"> <xsl:choose> <xsl:when test="boolean(@height)"> <xsl:value-of select="@height"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="31"/> </xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:attribute name="width"> <xsl:choose> <xsl:when test="boolean(@width)"> <xsl:value-of select="@width"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="88"/> </xsl:otherwise> </xsl:choose> </xsl:attribute> </img> </xsl:template>
xsl:for-eachを使う(2006.08.27追記)
このようなやりかたを教えていただきました.ありがとうございます.
指定してある属性を全てコピーして,デフォルトが必要な属性の指定がなかったら付け加えます.
<xsl:template match="/root/banner"> <img class="banner"> <xsl:for-each select="@*"> <xsl:attribute name="{name()}"> <xsl:value-of select="." /> </xsl:attribute> </xsl:for-each> <xsl:if test=".[not(@width)]"> <xsl:attribute name="width"> <xsl:value-of select="88"/> </xsl:attribute> </xsl:if> <xsl:if test=".[not(@height)]"> <xsl:attribute name="height"> <xsl:value-of select="31"/> </xsl:attribute> </xsl:if> </img> </xsl:template>
for-eachを使わずに書くこともできます.
<xsl:template match="/root/banner"> <img class="banner"> <xsl:apply-templates select="@*"/> <xsl:if test=".[not(@width)]"> <xsl:attribute name="width"> <xsl:value-of select="88"/> </xsl:attribute> </xsl:if> <xsl:if test=".[not(@height)]"> <xsl:attribute name="height"> <xsl:value-of select="31"/> </xsl:attribute> </xsl:if> </img> </xsl:template> <xsl:template match="@*"> <xsl:attribute name="{name()}"> <xsl:value-of select="." /> </xsl:attribute> </xsl:template>