【8/27まで】Udemyの人気コースが今なら1,200円から!!

【xslt入門】xslからhtmlを生成する

xsltとは

xsltとは、Extensible Stylesheet Languageの略で、データを木構造で格納したxml文書を処理する技術のことです。
雛形であるxslにxmlのデータを流し込むとアウトプットとしてhtmlが生成されるイメージ。

実行例

サンプルとして、以下のような階層構造を持ったxmlを用意します。

<?xml version="1.0" encoding="UTF-8"?>
<person>
    <item class="employee" id="00001">
        <name>一郎</name>
        <age>20</age>
        <phone type="private">111-111-111</phone>
        <phone type="business">100-000-000</phone>
    </item>
    <item class="employee" id="00002">
        <name>二郎</name>
        <age>30</age>
        <phone type="private">222-222-222</phone>
        <phone type="business">200-000-000</phone>
    </item>
    <item class="director" id="00003">
        <name>三郎</name>
        <age>40</age>
        <phone type="private">333-333-333</phone>
        <phone type="business">300-000-000</phone>
        <phone type="secret">999-999-999</phone>
    </item>
</person>

以下のxslファイルを用意して実行します。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="html" indent="yes" encoding="UTF-8"/>

	<!-- ルート階層-->
	<xsl:template match="/">
    <html>
        <body>
            <xsl:apply-templates />
        </body>
    </html>
	</xsl:template>
	
	<!-- /person階層 -->
	<xsl:template match="person">
        <xsl:apply-templates />
	</xsl:template>

    <!-- /person/item階層-->
    <xsl:template match="item">
        <xsl:apply-templates select="name"/>
        <xsl:apply-templates select="age"/>
        <xsl:apply-templates select="phone[@type='private']"/>
    </xsl:template>

    <!-- /person/item/name階層 -->
    <xsl:template match="name">
        <p>名前:<xsl:value-of select="."/></p>
    </xsl:template>

    <!-- /person/item/age階層 -->
    <xsl:template match="age">
        <p>年齢:<xsl:value-of select="."/></p>
    </xsl:template>

    <!-- /person/item/phone@private階層 -->
    <xsl:template match="phone[@type='private']">
        <p>電話番号(私用):<xsl:value-of select="."/></p>

        <!-- 1階層上を参照 -->
        <p>電話番号(仕事):<xsl:value-of select="../phone[@type='business']"/></p>
        <p>1階層上の年齢:<xsl:value-of select="../age"/></p>
    </xsl:template>

</xsl:stylesheet>

すると結果は以下のようになります。

<html>
    <body>
            
        <p>名前:一郎</p>
        <p>年齢:20</p>
        <p>電話番号(私用):111-111-111</p>
        <p>電話番号(仕事):100-000-000</p>
        <p>一回層上の年齢:20</p>
            
        <p>名前:二郎</p>
        <p>年齢:30</p>
        <p>電話番号(私用):222-222-222</p>
        <p>電話番号(仕事):200-000-000</p>
        <p>一回層上の年齢:30</p>
            
        <p>名前:三郎</p>
        <p>年齢:40</p>
        <p>電話番号(私用):333-333-333</p>
        <p>電話番号(仕事):300-000-000</p>
        <p>一回層上の年齢:40</p>
        
    </body>
</html>

select属性

マッチしている現在の要素を指す場合

<xsl:value-of select="."/></p>

もしくはcurrent()を使用して以下のとおり。

<xsl:value-of select="current()"/>

マッチしている要素の子要素を指す場合

<xsl:apply-templates select="name"/>

もしくは./を付けて以下のとおり。

<xsl:apply-templates select="./name"/>

絶対パスで指定する場合

/から書き始めると絶対パスと解釈されます。
※ただし、以下の記述は最初に見つかったnameの値が取得されます。

<xsl:value-of select="/person/item/name"/>

相対パスで指定する場合

現在位置の1階層上を指すには以下のように記述します。

<xsl:value-of select="../age"/>

xsl:template要素

name属性またはmatch属性を指定する必要があります。
name属性を指定した場合は、属性値を使って、xsl:call-template要素を用いることで呼び出すことができます。
match属性を指定した場合は、xsl:apply-templates要素を用いることで呼び出すことができます。

定義の方法

    <!-- name属性の場合 -->
    <xsl:template name="aaa">
        <p>test</p>
    </xsl:template>

    <!-- match属性の場合 -->
    <xsl:template match="bbb">
        <p>test</p>
    </xsl:template>

xsl:call-template要素

xsl:call-template要素は名前付きテンプレートを呼び出すことができます。
名前付きテンプレートとは以下のようにname属性で定義されたテンプレートのことです。

<xsl:template name="itiran">
    <h1>社員一覧</h1> 
</xsl:template>

上記のテンプレートを呼び出す際は以下のように記述します。

<xsl:call-template name="itiran" />

そのため、<xsl:template match="/abc/def">のようにmatch属性で定義されたテンプレートを呼び出すことはできません。
match属性で定義されたテンプレートを呼び出すにはxsl:apply-templates要素を使用します。

xsl:apply-templates要素

xsl:apply-templates要素は、select属性で指定したパターンとマッチするテンプレートを呼び出します。
また、パターンにマッチするすべてのノードに対してテンプレートが適用されます。
つまり、xml側にノードが存在しない場合は、テンプレートは呼び出されません。

サンプルの階層構造からitem要素がclass="employee"のノードの値を取得したい場合、テンプレートは以下のように定義します。

<xsl:template match="/person/item[@class='employee']">
    <p><xsl:value-of select="./@id"/>,<xsl:value-of select="./name"/>,<xsl:value-of select="./age"/>,<xsl:value-of select="./phone[@type='private']"/></p>
</xsl:template>

上記のテンプレートを呼び出すには、xsl:apply-templates要素を使って以下のように記述します。

<div><xsl:apply-templates select="/person/item[@class='employee']"/></div>

xsl:value-of要素

xsl:value-of要素は、select属性に指定したパターンとマッチする値を取得します。
次郎さんの名前を取得したい場合は以下のように記述します。

<xsl:value-of select="/person/item[@id='00002']/name"/>

また三郎さんの仕事用の電話番号を取得したい場合は以下のように記述します。

<xsl:value-of select="/person/item[@id='00003']/phone[@type='business']"/>

なお、すべてのnameを取得しようとして<xsl:value-of select="/person/item/name"/>を実行しても、これはうまく動きません。最初にヒットした「一郎」のみが取得できます。
すべてのnameを取得するには後述するxsl:for-each要素を使用します。

xsl:for-each要素

xsl:for-each要素は、select属性の値にマッチするすべてのノードに対して繰り返し処理を行います。

item要素から全てのnameの値を取得するには以下のように記述します。

<xsl:for-each select="/person/item">
    <xsl:value-of select="./name"/>
</xsl:for-each>

xsl:if 要素

xsl:if 要素は、条件によって処理を分岐する要素です。
test属性には式を指定します。式の評価がtrueの場合のみ、xsl:if 要素の中身が実行されます。falseの場合は何も実行されません。
item要素のclassの属性の値が「employee」なら「従業員」、「director」なら「部長」を表示するには以下のように記述します。

<!-- item要素の数だけループ -->
<xsl:for-each select="/person/item">
    <p><xsl:value-of select="./name"/></p>
    <xsl:if test="@class='employee'">
        <p>従業員</p>
    </xsl:if>
    <xsl:if test="@class='director'">
        <p>部長</p>
    </xsl:if>
</xsl:for-each>

変数の宣言

変数を宣言するには2通りの方法があります。

xsl:param要素

以下のように記述します。属性typeの値を変数「type」に格納するという意味です。

<xsl:param name="type" select="@type"/>

任意の値を持たせるには以下のように記述します。appleという文字列を変数「str」に格納するという意味です。

<xsl:param name="str" >apple</xsl:param>

数値を指定する場合は、以下の記述方法でも設定できます。

<xsl:param name="size" select="16"/>

xsl:variable要素

以下のように記述します。属性typeの値を変数「type」に格納するという意味です。

<xsl:variable name="type" select="@type"/>

任意の値を持たせるには以下のように記述します。lemonという文字列を変数「str」に格納するという意味です。

<xsl:variable name="str" >lemon</xsl:variable>

数値を指定する場合は、以下の記述方法でも設定できます。

<xsl:variable name="size" select="16"/>

java側で変数に値をセットする。

java側で変数に初期値をセットしてxslファイルに渡すには、 transformer.setParameter();を使用します。 Java側のサンプルは以下のとおり。

    public static void main(String[] args) {
        try {
            // XSLTファイルの指定
            StreamSource template = new StreamSource("sampleXSLT.xsl");

            // XMLファイルの指定
            StreamSource xml = new StreamSource("sampleXML.xml");

            // 出力ファイルの指定
            StreamResult result = new StreamResult(new FileOutputStream("result.xml"));

            // XSLTプロセッサのファクトリの生成
            TransformerFactory tFactory = TransformerFactory.newInstance();

            // XSLTプロセッサの生成
            Transformer transformer = tFactory.newTransformer(template);

            // 変数の初期化
            transformer.setParameter("fruits", "apple");

            // 実行
            transformer.transform(xml, result);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

xsl側のサンプルは以下のとおり。xsl:variable要素だと値を受け取れないようなので、xsl:param要素で変数宣言します。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="html" indent="yes" encoding="UTF-8"/>

    <xsl:param name="fruits" select="100"/>

	<!-- ルート階層-->
	<xsl:template match="/">
    <html>
        <body>
            <xsl:value-of select="$fruits"/>     
        </body>
    </html>
        
	</xsl:template>

</xsl:stylesheet>

結果は以下のとおりです。

<html>
    <body>apple</body>
</html>

xsl:choose要素とxsl:when要素とxsl:otherwise要素

xsl:choose要素とxsl:when要素とxsl:otherwise要素はセットで使用します。
いわゆるcase文に相当します。なおxsl:otherwise要素は省略可能です。

<!-- item要素の数だけループ -->
<xsl:for-each select="/person/item">
    <p><xsl:value-of select="./name"/></p>
    
    <!-- phone要素の数だけループ -->
    <xsl:for-each select="./phone">

        <!-- phone要素のtype属性の値を変数「type」に格納 -->
        <xsl:variable name="type" select="@type"/>

        <!-- 条件分岐処理 -->
        <xsl:choose>
            <xsl:when test="$type='private'">プライベート用</xsl:when>
            <xsl:when test="$type='business'">仕事用</xsl:when>
            <xsl:otherwise>秘密の電話番号 </xsl:otherwise>
        </xsl:choose>
    </xsl:for-each>
</xsl:for-each>

count関数

例えば、xml内にphone要素がいくつあるのかをカウントして表示するには以下のように記述します。
phoneの先頭に//を付与して//phoneと指定します。

<xsl:variable name="n" select="count(//phone)"/>
<xsl:value-of select="$n"/>

position関数

同じ要素が何回登場するか。つまり同名の兄弟要素がいくつあるかをカウントしてくれる。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="html" indent="yes" encoding="UTF-8"/>

	<!-- ルート階層-->
	<xsl:template match="/">
    <html>
        <body>
            <xsl:apply-templates />
        </body>
    </html>
	</xsl:template>
	
	<!-- /person階層 -->
	<xsl:template match="person">
        <xsl:apply-templates />
	</xsl:template>

    <!-- /person/item階層-->
    <xsl:template match="item">
        <xsl:apply-templates select="phone"/>
    </xsl:template>

    <!-- /person/item/name階層 -->
    <xsl:template match="phone">
        <p>電話番号:<xsl:value-of select="current()"/></p>
        <p>Position:<xsl:value-of select="position()"/></p>
    </xsl:template>

</xsl:stylesheet>

実行結果は以下のとおり。パターンにマッチしてテンプレートが実行されるたびに1からの採番になる。

<html>
    <body>
            
        <p>電話番号:111-111-111</p>
        <p>Position:1</p>
        <p>電話番号:100-000-000</p>
        <p>Position:2</p>
            
        <p>電話番号:222-222-222</p>
        <p>Position:1</p>
        <p>電話番号:200-000-000</p>
        <p>Position:2</p>
            
        <p>電話番号:333-333-333</p>
        <p>Position:1</p>
        <p>電話番号:300-000-000</p>
        <p>Position:2</p>
        <p>電話番号:999-999-999</p>
        <p>Position:3</p>
        
    </body>
</html>

xsl:element要素

要素を生成します。

<xsl:element name="html">
    <xsl:element name="body">
        <xsl:element name="div">test</xsl:element>
    </xsl:element>
</xsl:element>

上記を実行すると、以下のように生成されます。

<html>
    <body>
        <div>test</div>
    </body>
</html>

xsl:attribute要素

要素に属性を付与します。

<xsl:variable name="str" >lemon</xsl:variable>
<xsl:element name="html">
    <xsl:element name="body">
        <xsl:element name="div">
            <xsl:attribute name="class">
                <xsl:value-of select="$str"/>
            </xsl:attribute>
        </xsl:element>
    </xsl:element>
</xsl:element>

上記を実行すると、以下のように生成されます。

<html>
    <body>
        <div class="lemon"></div>
    </body>
</html>

コメントを残す

メールアドレスが公開されることはありません。