読者です 読者をやめる 読者になる 読者になる

XSLTなんて久しぶりに書いた

XML XSLT

2ちゃんを徘徊していたら、たまたまこんなの見つけた。

XML+XSLTでインライン要素ってどうやって実現するの? 
例えば、 

<top> 
<block> 
テキスト<link url="http://example.jp">リンク</link> 
</block> 
</top> 

というXMLがあったときにXSLT側はどうすれば 

<p> 
テキスト<a href="http://example.jp">リンク</a> 
</p> 

のようにできるの? 

<top> 
<block> 
<text>テキスト</text><link url="http://example.jp">リンク</link> 
</block> 
</top> 

とは書きたくないんだけど。
XML使いのスレ2.0

うは、昔の自分を見てるようだw
確か3年の頃だったか、同じような問題にはまっていた。


XSLTなんてそれ以来だけど、適当に書いてみた。
まず、対象となるXMLはこんな感じ。

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="style.xsl" type="text/xsl"?>

<page title="test">
    <link url="http://www.google.co.jp">リンク</link>とか、
    画像<img url="http://hogehost/image/image.png" alt="hoge" w="80" h="15"/>とか、
    バナーとか<banner base="http://hogehost/"
                img="image/image.png"
                link="index.html" alt="hoge" w="80" h="15"/><br/>
    改行とか、<strong>強調</strong>とか。
</page>

で、肝心のXSLT*1

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:apply-templates /> 
    </xsl:template>
    
    <xsl:template match="page">
        <html>
            <head>
                <title><xsl:value-of select="@title"/></title>
            </head>
            <body><p>
                <xsl:call-template name="body"/>
            </p></body>
        </html>
    </xsl:template>
    
    <!-- ページ本体部分のテンプレート -->
    <xsl:template name="body">
        <!-- 子要素の名前によって適用するテンプレートを分岐 -->
        <xsl:for-each select="node()">
            <xsl:choose>
                <xsl:when test="name(.)='link'">
                    <xsl:call-template name="link"/>
                </xsl:when>
                <xsl:when test="name(.)='img'">
                    <xsl:call-template name="img"/>
                </xsl:when>
                <xsl:when test="name(.)='banner'">
                    <xsl:call-template name="banner"/>
                </xsl:when>
                <xsl:when test="name(.)='strong'">
                    <xsl:call-template name="strong"/>
                </xsl:when>
                <xsl:when test="name(.)='br'">
                    <br/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="."/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each>
    </xsl:template>
    
    <!-- ハイパーリンクのテンプレート -->
    <xsl:template name="link">
        <a>
            <xsl:attribute name="href">
                <xsl:value-of select="@url"/>
            </xsl:attribute>
            <xsl:value-of select="."/>
        </a>
    </xsl:template>
    
    <!-- 画像のテンプレート -->
    <xsl:template name="img">
        <img>
            <xsl:attribute name="src">
                <xsl:value-of select="@url"/>
            </xsl:attribute>
            <xsl:attribute name="alt">
                <xsl:value-of select="@alt"/>
            </xsl:attribute>
            <xsl:attribute name="width">
                <xsl:value-of select="@w"/>
            </xsl:attribute>
            <xsl:attribute name="height">
                <xsl:value-of select="@h"/>
            </xsl:attribute>
        </img>
    </xsl:template>
    
    <!-- バナー(リンク+画像)のテンプレート -->
    <xsl:template name="banner">
        <a>
            <xsl:attribute name="href">
                <xsl:value-of select="@base"/><xsl:value-of select="@link"/>
            </xsl:attribute>
            <img>
                <xsl:attribute name="src">
                    <xsl:value-of select="@base"/><xsl:value-of select="@img"/>
                </xsl:attribute>
                <xsl:attribute name="alt">
                    <xsl:value-of select="@alt"/>
                </xsl:attribute>
                <xsl:attribute name="width">
                    <xsl:value-of select="@w"/>
                </xsl:attribute>
                <xsl:attribute name="height">
                    <xsl:value-of select="@h"/>
                </xsl:attribute>
            </img>
        </a>
    </xsl:template>
    
    <!-- テキストを強調表示する部分のテンプレート -->
    <xsl:template name="strong">
        <strong>
            <xsl:value-of select="."/>
        </strong>
    </xsl:template>
    
</xsl:stylesheet>

このXSLTで、最初のXMLがこんな感じに変換される。

<html>
    <head>
        <title>test</title>
    </head>
    <body><p>
        <a href="http://www.google.co.jp">リンク</a>とか、
        画像<img src="http://hogehost/image/image.png" alt="hoge" width="80" height="15"/>、
        バナーとか<a href="http://hogehost/index.html">
        <img src="http://hogehost/image/image.png" alt="hoge" width="80" height="15"/>
        </a><br/>
        改行とか、<strong>強調</strong>とか。
    </p></body>
</html>

いろいろと手抜きだけど、一応動く。
肝心な部分は、bodyテンプレートで、node関数使って子要素を全部取得、その一つ一つの要素に適用すべきテンプレートを選んでいる。
このテンプレートはmatch属性じゃなくてname属性を使ってるから、page要素以外にブロック要素を作ったとしても、そこから呼び出せば動く・・・はず。


XSLTも面白くはあるんだけど、なんと言うか、間抜けだよね。がんばってXSLT書いたところで独自規格でしか無いわけだし。
この質問者はここから更にDTDやらXML Schemaやらやりだすと予言w

*1:当時はXSLとXSLTの違いが分からなかったのもいい思い出だ