XMLマスターへの道
〜「XMLマスター:ベーシック」試験対策〜

第10回 XSLTの基本構造を理解する

内藤一彦
NRIラーニングネットワーク株式会社
2003/10/10

 XML文書の変換を行うためのXSLTについて、本稿より2回にわたって解説します。今回は、基本的なXSLTの使用方法を紹介します。前回「第9回 DTDの実体宣言と記法宣言」で予習問題として下記の問題を出題しておきました。この問題を解くための解説をした後、解答を示します。

今回の問題

(Q1) 次のXSLT要素のうちトップレベル要素となれるものをすべて選択してください。

(a xsl:attribute-set
(b) xsl:param
(c) xsl:template
(d) xsl:copy
(e) xsl:value-of
(f)  xsl:apply-templates

(Q2) 次のXSLTスタイルシートが処理されるとき、最初に実行されるテンプレートはどれでしょう。

XSLTスタイルシート文書
<xsl:stylesheet
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 version="1.0">

<xsl:template match="/">
 <HTML>
    <HEAD></HEAD>
    <BODY>
       <TABLE BORDER="1">
           <TR>
              <TD>社員番号</TD>
              <TD>社員名</TD>
           </TR>
           <xsl:for-each select="/EMPLIST/EMPLOYEE">
           <TR>
             <TD>
               <xsl:value-of select="@id"/>
             </TD>
             <TD>
               <xsl:apply-templates select="NAME"/>
             </TD>
           </TR>
           </xsl:for-each>
       </TABLE>
  </BODY>
 </HTML>
</xsl:template>

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

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

</xsl:stylesheet>

(a)
<xsl:template match="/">のテンプレート
(b)
<xsl:template match="*">のテンプレート
(c)
<xsl:template match="text()">のテンプレート
(d)
変換を行う際に明示的に指定する


 今回は、この問題に解答するうえで必要となる下記の内容について解説します。

  • XSLTの概要
  • XSLTの基本構造
    • 基本構造、トップレベル要素
    • テンプレート
    • テンプレートの処理順序
    • テンプレートの競合

XSLTの概要

 XSLT(XSL Transformations)は、1999年にW3Cで勧告された標準仕様でXML文書の構造変換を行うための機能です。

 XSLTの使用環境は次のようになります。

図1 XSLTの使用環境

 図1のXSLTプロセッサは、XSLT仕様に基づいた変換を行うためのプログラムです。GUIベースのソフトウェアからXMLパーサに組み込まれているプログラムベースで使用するソフトウェアまで、有償/無償のさまざまな製品があります。どんな種類があるか興味のある方は、OASISソフトウェア紹介のWebページをご覧になるとよいでしょう。

 このXSLTプロセッサに、変換元となるXML文書と変換内容を定義しているXSLTスタイルシート文書を読み込ませて、変換処理を行わせます。何を隠そう、XSLTスタイルシート文書もXML形式をとります。

 つまり、XSLTで定められている要素や属性を使用して、XML形式のドキュメントとして変換内容を定義することになります。


XSLTの基本構造

基本構造、トップレベル要素

 まずはサンプル文書を使って、実際に変換を行ってみましょう。

<?xml version="1.0" encoding="Shift_JIS" ?>
<EMPLOYEES>
  <EMPLOYEE empid="A001">
    <Name>Kazuhiko Naito</Name>
    <Dept>IT Education Services Dept.</Dept>
  </EMPLOYEE>
  <EMPLOYEE empid="B002">
    <Name>Masahiko Kondo</Name>
    <Dept>Education Sales Dept.</Dept>
  </EMPLOYEE>
</EMPLOYEES>
リスト1 変換元のXML文書(employee.xml

<?xml version="1.0" encoding="Shift_JIS" ?>
<xsl:stylesheet
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<社員一覧>
   <xsl:apply-templates select="/EMPLOYEES/EMPLOYEE" />
</社員一覧>
</xsl:template>

<xsl:template match="EMPLOYEE">
  <社員>
    <社員番号><xsl:value-of select="@empid"/></社員番号>
    <氏名><xsl:value-of select="Name"/></氏名>
    <部署名><xsl:value-of select="Dept"/></部署名>
  </社員>
</xsl:template>

</xsl:stylesheet>
リスト2 XSLTスタイルシート文書(employee.xsl

 なお、簡単に変換結果を確認したいのであれば、「第5回 valid XMLとDTDの関係」で紹介されているMicrosoft社のInternet Explorer Tools for Validating XML and Viewing XSLT OutputのViewing XSL Outputを使うとよいでしょう。

Internet Explorer Tools for Validating XML and Viewing XSLT Outputのインストール手順
ダウンロードするファイルはiexmltls.exeです。これを解凍すると、デフォルトでC:\IEXMLTLSが作成されます。このフォルダ内にある「msxmlval.inf」「msxmlvw.inf」をそれぞれ右クリックして「インストール」を選びます。IEを再起動させると、右クリックから「Validate XML」「 View XSL Output」の2つのメニューが有効になります。

 このツールを使用する場合、変換元のXML文書で次のような命令を挿入してください。

<?xml version="1.0" encoding="Shift_JIS" ?>
<?xml-stylesheet type="text/xsl" href="employee.xsl" ?>
<EMPLOYEES>
 :
 :
リスト3 Viewing XSLT Output用の追加命令(employee2.xml

 このXMLをIE(Internet Explorer)で開いた後に、ウィンドウ内で右クリックしメニューから「View XSL Output」を実行します。

図2 View XSL Outputでの表示内容

 では、いよいよXSLTスタイルシートを見ていきましょう。まず、着目すべきは、「XSLTスタイルシートはXML形式である」ということです。次の構造になっていることを押さえておきましょう。

図3 employee.xslの構造

 XML文書では必ず1つのルート要素がありますが、XSLTではstylesheet要素を使用します。次のような記述をします。

<xsl:stylesheet  
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  <--(1)
 version="1.0"  <--(2)(注)
>

stylesheet要素の記述内容
(1)XSLT名前空間の宣言、(2)XSLTバージョンの宣言
(注)現在使用されているバージョンは「1.0」ですが、次バージョンである「2.0」が策定中です。

 stylesheet要素の子としてtemplate要素やoutput要素などトップレベル要素を記述します。トップレベル要素とはstylesheet要素の直接の子要素となれるものを指し、次の種類があります。

attribute-set decimal-format import
include  key namespace-alias
output  param preserve-space
strip-space  template variable

テンプレート

 実際の変換内容は、トップレベル要素であるtemplate要素内に記述していくことになります。employee.xslには、2つのテンプレートブロックが存在しています。これらのテンプレートが呼び出され、変換処理が行われていきます。

<xsl:template match="/">
  <社員一覧>
    <xsl:apply-templates select="/EMPLOYEES/EMPLOYEE" />
  </社員一覧>
</xsl:template>

テンプレート1


<xsl:template match="EMPLOYEE">
  <社員>
    <社員番号><xsl:value-of select="@empid"/></社員番号>
    <氏名><xsl:value-of select="Name"/></氏名>
    <部署名><xsl:value-of select="Dept"/></部署名>
  </社員>
</xsl:template>

テンプレート2

 template要素のmatch属性では、どのノードを処理するためのものかを指定します。つまり、テンプレート1は「/(ルート)」(注)を変換するため、テンプレート2は「EMPLOYEE要素」を処理するためのテンプレートになります。

(注) /(ルート)は、ルート要素(EMPLOYEES要素)ではありません。ルート要素の親に当たり、XML文書の階層構造で考えると最上位になります。すべてのものはこの子孫に含まれるようになるので、/(ルート)を変換するということは、文書全体を変換することになります。

テンプレートの処理順序

 では、このスタイルシートでの処理の流れを確認しましょう。

(1)/(ルート)用のテンプレートの呼び出し
 実際に変換処理が行われる際には、必ず、/(ルート)用のテンプレートが最初に呼び出され処理されます。従って、テンプレート1が呼び出されることになります。

(2)/(ルート)用のテンプレート内の処理
 このテンプレートでは、次のことを行っています。

  • 「社員一覧」要素の作成
  • EMPLOYEE用テンプレートの呼び出し

 テンプレート内を見ると、「社員一覧」という開始タグ・終了タグがあります。このように、直接タグ形式で記述すると、変換により要素が作成されます。もし、HTMLに変換するためのスタイルシートを作成したいのであれば、直接HTMLタグを埋め込めばよいわけです(HTMLに変換するためのスタイルシートのサンプル)。

 直接タグを記述するほかに、XSLTのelement要素を使って、要素を作成することもできます。

<xsl:template match="/">
<xsl:element name="社員一覧">
   <xsl:apply-templates select="/EMPLOYEES/EMPLOYEE" />
</xsl:element>
</xsl:template>

xsl:elementを使用した例

 さて、これで「社員一覧」要素が作成されますが、開始タグ・終了タグの間を見てみましょう。apply-templates要素が記述されています。select属性で処理したいノードを指定し、そのノード用のテンプレートを呼び出します。つまり、EMPLOYEEを処理するためのテンプレートを呼び出すことになります。

 「社員一覧」要素の開始タグ・終了タグの間に記述されているので、最終的には呼び出したテンプレートの処理結果が「社員一覧」要素の子として挿入されます。

(3)EMPLOYEE用のテンプレートの呼び出し
 前述のapply-templates要素により呼び出されるのが、テンプレート2(match="EMPLOYEE"のxsl:template)です。変換元のemployee.xmlでは、EMPLOYEEが2つありますので、それぞれに対してテンプレートが呼び出されます。

(4)EMPLOYEE用のテンプレート内の処理
 このテンプレートでは、次のことを行っています。

  • 「社員」要素の作成
  • 「社員番号」「氏名」「部署名」要素の作成
  • 値の取得

 テンプレート1と同様、タグを直接記述し新しい要素を作成しています。

 「社員番号」「氏名」「部署名」の開始タグ・終了タグの間では、それぞれ、value-of要素が使われています。この命令を使用するとselect属性で指定されたノードの値を取得することができます。それぞれのvalue-ofの結果が「社員番号」「氏名」「部署名」要素の値として挿入されることになります。

 なお、属性名を指定する場合には、

<xsl:value-of select="@empid" />

のように、属性名に“@”を付けて記述します。

 ここまで、初歩的なXSLTスタイルシートの構造について解説してきました。いままで取り上げたXSLT要素をまとめておきます。

  • xsl:stylesheet(XSLTスタイルシートのルート要素)
    XSLTのバージョンを宣言する(version="1.0")。
    XSLTの名前空間を宣言する(xmlns:xsl="http://www.w3.org/1999/XSL/Transform")。
  • xsl:apply-templates(テンプレートブロックの呼び出しを行う)
    テンプレートを呼び出し処理するノードを指定する(select="/EMPLOYEES/EMPLOYEE")。
    select属性は省略することもできる。その場合には、カレントノード(現在の作業位置)から見てすべての子ノードに対して呼び出しをかける。
  • xsl:template(テンプレートブロックの定義を行う)
    どのノードを処理するためのテンプレートか指定する(match="EMPLOYEE")。
  • xsl:value-of(値を取得する)
    値を取り出したいノードを指定する(select="@empid")。

テンプレートの競合

 xsl:apply-templates要素命令を使用してテンプレートを呼び出すとき、次のテンプレートが存在していた場合にはどうなるでしょう。どちらも呼び出されるだけの条件はそろっています。

 :
<xsl:template match="/EMPLOYEES/EMPLOYEE">
  <社員>
    <氏名><xsl:value-of select="Name"/></氏名>
  </社員>
</xsl:template>

<xsl:template match="EMPLOYEE">
  <社員>
    <社員番号><xsl:value-of select="@empid"/></社員番号>
    <氏名><xsl:value-of select="Name"/></氏名>
    <部署名><xsl:value-of select="Dept"/></部署名>
  </社員>
</xsl:template>
 :
リスト4 テンプレートが競合している(employeepri.xslの一部)

 このスタイルシートを適用させるXML文書(employeepri.xml)をIEで開いた後、View XSL Outputで表示した結果は次のとおりです。

図4 競合するテンプレートを適用させた結果

 図4を見ると、match="/EMPLOYEES/EMPLOYEE"のテンプレートが呼び出されていることが分かります。このように呼び出されるテンプレートの候補が複数ある場合、テンプレートの優先度に従って、最も優先度の高いどれか1つのみが呼び出され処理されます。この優先度を決定するのがxsl:templateのpriority属性です。

<xsl:template match="EMPLOYEE" priority="1.0">

priority属性を指定する記述

 priority値は正の値の方が高く、負の値ほど低くなります。priorityを省略している場合には、match属性でのノードの指定パターンにより省略時解釈の値がとられます。省略時解釈の値の範囲は、−0.5〜0.5です。細かい指定をしたときほどpriority値は高く、ワイルドカードを使っているような場合には低いpriority値が設定されます。

 この例で使われているテンプレートのpriority値は

テンプレート名 priority値
match="EMPLOYEE" 0
match="/EMPLOYEES/EMPLOYEE" 0.5

となります。

 先ほどのテンプレートにpriority属性を加えてみました。

 :
<xsl:template match="/EMPLOYEES/EMPLOYEE">
  <社員>
    <氏名><xsl:value-of select="Name"/></氏名>
  </社員>
</xsl:template>

<xsl:template match="EMPLOYEE" priority="1.0">
  <社員>
    <社員番号><xsl:value-of select="@empid"/></社員番号>
    <氏名><xsl:value-of select="Name"/></氏名>
    <部署名><xsl:value-of select="Dept"/></部署名>
  </社員>
</xsl:template>
 :
リスト5 priority値1.0を追加(employeepri2.xslの一部)

 テンプレートにpriority属性を加えてemployeepri2.xmlをIEで開いた後、View XSL Outputで表示した結果は次のとおりです。

図5 priority属性の高いテンプレートが適用された

 もし、意図的に優先度を上げたいテンプレートがあるのなら、省略時にとられるpriority値よりも高い値(0.5より高い値)を指定するとよいでしょう。

今回の問題の解答

 それでは、問題の解答を解説します。

(Q1) 次のXSLT要素のうちトップレベル要素となれるものをすべて選択してください。

 答えは、(a)、(b)、(c)です。

 この問題に関して参考になる解説は、「XSLTの基本構造(基本構造、トップレベル要素)」です。参考までに、選択肢に出ているXSLT要素命令を紹介しておきます。

XSLT要素命令 意味
xsl:attribute-set 属性グループを定義する。xsl:elementを使用して要素を作成するときに、これで定義しておいた属性グループ名を指定できる
xsl:param XSLTスタイルシートの中で扱うパラメータ(変数)を定義する
xsl:copy 変換元XMLからカレントノードをコピーする

(Q2) 次のXSLTスタイルシートが処理されるとき、最初に実行されるテンプレートはどれでしょう。

 答えは、(a)です。

 この問題に関して参考になる解説は、「XSLTの基本構造(テンプレートの処理順序)」です。

 (b)<xsl:template match="*">で使用されている"*"はすべての要素を指します。従って、このテンプレートはすべての要素を処理するために使用できます。

 (c)<xsl:template match="text()">で使用されている"text()"はすべてのテキストノードを指します。テキストノードに当たるのは、要素の値です。XSLTで使用されるノードモデルでは、「要素の値」は要素の子ノードであるテキストノードが持つようになっています。いままで要素の値を取得するために使用していた<xsl:value-of select="Name" />は、<xsl:value-of select="Name/text()" />としても同じ結果が得られます。

 問題で使用されているスタイルシートのフローは次のようになります。

<xsl:stylesheet
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">  …(1)
 <HTML>
    <HEAD></HEAD>
    <BODY>
       <TABLE BORDER="1">
           <TR>
              <TD>社員番号</TD>
              <TD>社員名</TD>
           </TR>
           <xsl:for-each select="/EMPLIST/EMPLOYEE">  …(2)
           <TR>
             <TD>
               <xsl:value-of select="@id"/>  …(3)
             </TD>
             <TD>
               <xsl:apply-templates select="NAME"/>  …(4)
             </TD>
           </TR>
           </xsl:for-each>
       </TABLE>
  </BODY>
 </HTML>
</xsl:template>

<xsl:template match="*">    …(5)
    <xsl:apply-templates/>  …(6)
</xsl:template>

<xsl:template match="text()">    …(7)
    <xsl:value-of select="."/>   …(8)
</xsl:template>

</xsl:stylesheet>
リスト6 解析対象外とする外部ファイルとその記法宣言の記述例
  1. /(ルート)用のテンプレートが最初に呼び出され処理されます。
  2. xsl:for-each 要素は、select属性で指定されているノードを取り出しながらループします。終了タグである</xsl:for-each>までがループ内の処理です。
  3. id属性の値を取得します。
  4. Name要素を処理するためにテンプレートを呼び出します。
  5. ここで呼び出されるのは、match="*"のテンプレートとなります。
  6. xsl:apply-templates要素でテンプレートを呼び出しますが、select属性が記述されていません。この場合、カレントノード(この場合、Name要素がカレントになっています)の子ノードすべてに対してテンプレート呼び出しを行います。Name要素が値を持っているとすると、この子ノードとして考えられるのは、テキストノードです。従って、テキストノード用のテンプレートを呼び出します。
  7. ここで呼び出されるのが、match="text()"のテンプレートです。
  8. カレントノード(.)の値を取り出します。xsl:apply-templates要素命令では、テンプレートを呼び出したときに、カレントノードを移動させますので、この時点では、カレントはテキストノードにあります。従って、カレントであるName要素の値が取得されます。

次回の予習問題

 次回のための予習問題を掲載します。

 次回も引き続きXSLTです。XSLTにはプログラム言語にあるような基本制御命令が用意されています。その中でも押さえておきたいいくつかの命令について解説していきます。

予習問題

(Q1) 次のような学生一覧のXML文書があるときに、学年・クラス・出席番号の順でソートを行い処理したい。このときの、ソートの記述方法として正しいのはどれでしょう。

学生一覧XML
<Students>
  <Student grade="2" class="A" no="16" >Naito</Student>
  <Student grade="1" class="B" no="11" >Ito</Student>
  <Student grade="2" class="A" no="28" >Saito</Student>
  <Student grade="2" class="A" no="1" >Bito</Student>
  <Student grade="2" class="C" no="15" >Kato</Student>
</Students>

(a)
<xsl:for-each select="Student" order="@grade"
 order="@class" order="@no">
    繰り返す処理内容
</xsl:for-each>

(b)
<xsl:for-each select="Student">
  <xsl:sort select="@grade" >
    <xsl:sort  select="@class">
      <xsl:sort  select="@no">
        繰り返す処理内容
      </xsl:sort>
    </xsl:sort>
  </xsl:sort>
</xsl:for-each>

(c)
<xsl:sort select="@grade" >
  <xsl:sort  select="@class">
    <xsl:sort  select="@no">
      <xsl:for-each select="Student">
        繰り返す処理内容
      </xsl:for-each>
    </xsl:sort>
  </xsl:sort>
</xsl:sort>

(d)
<xsl:for-each select="Student">
  <xsl:sort select="@grade" />
  <xsl:sort  select="@class" />
  <xsl:sort  select="@no" />
  繰り返す処理内容
</xsl:for-each>

(Q2) 組み込みテンプレートとして正しいものをすべて選択してください。


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

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

(c)
<xsl:template  
 match="processinginstruction()|comment()" />

(d)
<xsl:template  match="node()" />

まとめノート:XSLTの基本構造を理解する

○今回の試験対策のポイント

  • XSLTの概要
  • XSLTの基本構造
    • 基本構造、トップレベル要素
    • テンプレート
    • テンプレートの処理順序
    • テンプレートの競合

○今回の学習内容で出題範囲となる仕様

○今回の学習内容で実機確認を行うときに役立つフリーツール

○今回の学習内容で参考になる@ITの記事

○今回の学習内容で参考になるXML用語集 @IT XML用語事典より)

(11)XSLTで必須の制御命令を覚える

Index
連載:XMLマスターへの道
  (1)XMLマスター:ベーシック試験のレベルは?
  (2)XMLの概要と起源、関連規格
  (3)XML文書の要素、エンコーディング、宣言
  (4)すべてのXM文書は整形式である
  (5)valid XMLとDTDの関係
  (6)模擬問題:XMLの基本
  (7)模擬問題:W3C XML Schema
  (8)模擬問題:DOM
  (9)DTDの実体宣言と記法宣言
(10)XSLTの基本構造を理解する
  (11)XSLTで必須の制御命令を覚える
  (12)XPathによるノードの指定法を理解する
  (13)名前空間を理解しDOMの概要をつかむ
  (14)XML Schemaを利用したスキーマ定義
  (最終回)XML Schema―型の再利用と名前空間


連載:XMLマスターへの道


XML & SOA フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

HTML5+UX 記事ランキング

本日月間