1 前言
在第1章中,我们用一个简单的XSLT样式表作为例子介绍了XSLT的概要。当然,XSLT所拥有的元素并不仅限于xsl:template、xsl:apply-templates、xsl:value-of。为了实行转换,XSLT准备了各种各样的元素。本章将以最常用的元素为中心举例说明。
另外,本文中并不准备介绍元素的全部属性。详情请参见W3C标准的原文[1]及译文[2]等。
在XSLT中经常会用到一种被称为XPath的描述方法。首先从XPath的概要开始介绍。
2 XPath是什么?
XPath是树结构的一种描述方法。在创建XSL样式表时经常使用XPath。
2.1 树结构
XML文档表示的数据组成数结构。用XLST进行转换,也就是从源文档的树(源树)生成转换后的树(目标树)的意思。这个转换以树结构的节点为基础来进行。
节点有几种,主要为:
- 表示根的“根节点”
- 表示元素的“元素节点”
- 表示属性的“属性节点”
- 表示文本的“文本节点”
- 表示注释的“注释节点”
使用像这样的各种各样的节点来表示树的位置的描述方法称为XPath。
2.2 XPath表达式
创建XSLT样式表时经常使用的XPath表达式如表1所示。所谓的上下文节点,就是由上下文构成的节点,可以理解为“处理对象”。另外,今后会出现“当前节点”的概念,可以理解为“被选中的节点”。 1
记号 | 含义 |
---|---|
a | 上下文节点的a元素 |
* | 上下文节点的所有元素 |
a/b | 以上下文节点的a元素为父节点的b元素 |
a//b | 以上下文节点的a元素为祖先的b元素 |
a|b | 上下文节点的a元素和b元素 |
a[表达式] | 符合表达式的上下文节点的a元素 |
. | 上下文节点 |
.. | 上下文节点的父节点 |
/ | 根节点 |
@a | 上下文节点的a属性 |
@* | 上下文节点的所有属性 |
node() | 所有节点 |
text() | 文本节点 |
<html>
<body>
<a href="sample.html">示例</a>
<ul>
<li>项目A</li>
<li>项目B</li>
</ul>
</body>
</html>
/html/body/a
/html/body/a/@href
/html/body/ul/li[1]
3 定义样式表的元素
XSLT样式表使用XML文档的格式创建。因此,必须要遵从XML文档的描述规则。XML文档中必须存在的元素只有根元素。在XSLT样式表中的根元素就是xsl:stylesheet元素。基本代码如下所示:
<xsl:stylesheet
version = "版本号">
<!-- 内容: (xsl:import*, 顶层元素) -->
</xsl:stylesheet>
4 模板规则
XSLT样式表可以说是模板规则的集合。
4.1 模板规则的定义
模板规则使用xsl:template元素进行定义。它的属性包括match、name、priority和mode。其中最重要的是match属性,该属性规定了节点的样式。若没有指定name属性的话,就必须指定match属性。基本的代码如下所示。
<xsl:template
match = "样式"
name = "名称">
<!-- 内容: (xsl:param*, 模板) -->
</xsl:template>
4.2 应用模板规则
我们使用xsl:apply-template元素来应用模板规则。它包含select和mode属性。基本的代码如下所示:
<xsl:apply-templates
select = "节点集合表达式">
<!-- 内容: (xsl:sort | xsl:with-param)* -->
</xsl:apply-templates>
<xsl:apply-templates select="节点集合表达式"/>
通常,仅在处理当前节点的子孙节点时使用该元素。这样就不会发生无法终了的无限循环。不能定义如下例所示的无限循环模版:
<xsl:template match="x">
<xsl:apply-templates select="."/>
</xsl:template>
4.3 命名模版
带有name属性的模板规则可以通过模版名称来调用。
<xsl:call-template
name = "名称">
<!-- 内容: xsl:with-param* -->
</xsl:call-template>
<xsl:template name="hello">
你好
</xsl:template>
<xsl:call-template name="hello"/>
你好
4.4 匹配冲突
某个指定的表达式可能会出现多个匹配结果。这时根据优先度来决定应用哪个模板。定义模板规则时可通过设置priority属性来显式地指定模板优先度。如未指定,将采用默认优先度。关于默认优先度的计算方法请参见参考文献[1]。一般来说限制性强的表达式优先度较高。例如“a”的优先度要高于“*”。
4.5 内嵌模版规则
内嵌模板规则即为默认模板规则,不匹配任何模板规则的节点将由它来处理。这种机制保证了即使在不定义任何模板规则的情况下,模版处理也能递归地进行下去。
应用于元素节点和根节点的内嵌模板规则如下所示。
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()|@*">
<xsl:value-of select="."/>
</xsl:template>
这种模板规则将把文本节点内容全部输出。将代码1所示的XSLT样式表应用到某个XML文档上之后,将生成以doc为根节点、文本节点为内容的XML文档。
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<doc>
<xsl:apply-templates/>
</doc>
</xsl:template>
</xsl:stylesheet>
5 输出
虽然称为“输出”,但实际的意思是“生成目标树的节点”。可以使用源树生成节点,也可以直接生成节点。
5.1 取出文本
xsl:value-of元素可以将指定的节点的值作为字符串来输出。必须要指定select属性。
<xsl:value-of
select = "字符串表达式"/>
<p>.com时代的<b>IT</b>杂志</p>
<xsl:value-of select="p"/>
.com时代的IT杂志
<a href="http://www.gihyo.co.jp">
技术评论公司</a>
<xsl:value-of select="."/>的URL是
<xsl:value-of select="@href"/>。
技术评论公司的URL是
http://www.gihyo.co.jp。
5.2 节点复制
xsl:value-of元素将节点的值转换为字符串。与此相对,xsl:copy-of元素将复制节点,节点中包含的子元素原封不动。基本的代码如下所示。
<xsl:copy-of
select = "表达式"/>
<p>.com时代的<b>IT</b>杂志</p>
<xsl:copy-of select="p"/>
<p>.com时代的<b>IT</b>杂志</p>
5.3 属性值模板
例如,想由
<link>
<title>技术评论公司</title>
<url>http://www.gihyo.co.jp/</url>
</link>
<a href="http://www.gihyo.co.jp/">
技术评论公司
</a>
<xsl:template match="link">
<a href="{url}">
<xsl:value-of select="title"/>
</a>
</xsl:template>
5.4 生成文本
xsl:text元素可以生成文本节点。由于生成文本时直接将文本写出即可,所以一般情况下该元素不使用。但是若需要令输出的转义字符失效的话就需要使用该元素。基本代码如下所示。
<xsl:text
disable-output-escaping = "yes" | "no">
<!-- 内容: #PCDATA -->
</xsl:text>
<xsl:text disable-output-escaping="yes">
<
</xsl:text>
<
5.5 生成注释
需要生成注释时可以使用xsl:comment元素。
<xsl:commwnt>这里是注释。</xsl:comment>
<!--这里是注释。-->
5.6 复制
复制当前节点可以使用xsl:copy元素。属性和子节点不会被自动复制。基本代码如下所示。
<xsl:copy>
<!-- 内容:模板 -->
</xsl:copy>
<xsl:template match="content">
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
5.7 生成元素
需要生成元素时可以使用xsl:element元素。通常,只要将需要生成的元素直接写出即可,不必使用xsl:element元素。该元素仅在需要动态生成元素时才有必要使用。基本代码如下所示。
<xsl:element
name = "生成元素的名称">
<!-- 内容: 模板 -->
</xsl:element>
<heading>
<text>这是标题。</text>
<size>2</size>
</heading>
<xsl:template match="heading">
<xsl:element name="{concat('h', size)}">
<xsl:value-of select="text"/>
</xsl:element>
</xsl:template>
<h2>这是标题。</h2>
5.8 生成属性
需要生成属性时可以使用xsl:attribute元素。与生成元素的情况相同,通常只需要将需要生成的属性直接写出即可,不需使用xsl:attribute元素。该元素仅在需要动态生成属性等场合才有必要使用。基本代码如下所示。
<xsl:attribute
name = "生成属性的名称">
<!-- 内容:模板 -->
</xsl:attribute>
<link>
<title>技术评论公司</title>
<url>http://www.gihyo.co.jp/</url>
</link>
<a href="http://www.gihyo.co.jp/">
技術評論社
</a>
<xsl:template match="link">
<a>
<xsl:attribute name="href">
<xsl:value-of select="url"/>
</xsl:attribute>
<xsl:value-of select="title"/>
</a>
</xsl:template>
5.9 添加编号
需要输出整数值时可以使用xsl:number元素。基本代码如下所示。
<xsl:number
level = "源树的级别"
count = "表达式"
from = "表达式"
value = "数值表达式"
format = "表示格式的字符串" />
<items>
<item>A</item>
<item>D</item>
<item>B</item>
<item>C</item>
</items>
<xsl:template match="items">
<xsl:copy>
<xsl:apply-templates select="item"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item">
<xsl:copy>
<xsl:number/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
<items>
<item>1 A</item>
<item>2 D</item>
<item>3 B</item>
<item>4 C</item>
</items>
<xsl:template match="items">
<xsl:copy>
<xsl:for-each select="item">
<xsl:sort select="."/>
<xsl:copy>
<xsl:number/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<items>
<item>1 A</item>
<item>3 B</item>
<item>4 C</item>
<item>2 D</item>
</items>
<xsl:number value="position()"/>
例如,有下面这种层次结构的XML文档。
<chapter title="第一章">
<section title="第一节">
<subsection title="第一部分">
</subsection>
</section>
<section title="第二节">
<subsection title="第二部分">
</subsection>
<subsection title="第三部分">
</subsection>
</section>
</chapter>
<xsl:template match="chapter|section|subsection">
<xsl:apply-templates select="@title"/>
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="@title">
<xsl:number level="single"
count="chapter|section|subsection"/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/><br/>
</xsl:template>
1 第一章<br/>
1 第一节<br/>
1 第一部分<br/>
2 第二节<br/>
1 第二部分<br/>
2 第三部分<br/>
1 第一章<br/>
1.1 第一节<br/>
1.1.1 第一部分<br/>
1.2 第二节<br/>
1.2.1 第二部分<br/>
1.2.2 第三部分<br/>
1 第一章<br/>
2 第一节<br/>
3 第一部分<br/>
4 第二节<br/>
5 第二部分<br/>
6 第三部分<br/>
from属性设置计数开始的位置。例如,考虑如下的XML文档。从逻辑上看,各个节点之间有父子关系,但是在源树中各个节点是并列关系。
<h1>大标题一</h1>
<h2>中标题一</h2>
<h3>小标题一</h3>
<h2>中标题二</h2>
<h3>小标题二</h3>
<h3>小标题三</h3>
<xsl:template match="h1">
<xsl:number/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/><br/>
</xsl:template>
<xsl:template match="h2">
<xsl:number level="any" count="h1"/>
<xsl:text>.</xsl:text>
<xsl:number level="any" from="h1" count="h2"/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/><br/>
</xsl:template>
<xsl:template match="h3">
<xsl:number level="any" count="h1"/>
<xsl:text>.</xsl:text>
<xsl:number level="any" from="h1" count="h2"/>
<xsl:text>.</xsl:text>
<xsl:number level="any" from="h2" count="h3"/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/><br/>
</xsl:template>
1 大标题一<br/>
1.1 中标题一<br/>
1.1.1 小标题一<br/>
1.2 中标题二<br/>
1.2.1 小标题二<br/>
1.2.2 小标题三<br/>
字符串 | 格式范例 |
---|---|
1 | 1 2 ... 10 11 ... |
01 | 01 02 ... 09 10 ... 99 100 ... |
A | A B ... Z AA AB ... |
a | a b ... z aa ab ... |
i | i ii iii iv v vii viii ix x ... |
I | I II III IV V VII VIII IX X ... |
6 循环
需要循环进行处理时可以使用xsl:for-each元素。基本代码如下所示。
<xsl:for-each
select = "节点集合">
<!-- 内容:(xsl:sort*, 模板) -->
</xsl:for-eaxh>
例如,如下所示,bookmark元素中包含了多个link元素。
<bookmark>
<link>
<title>技术评论公司</title>
<url>http://www.gihyo.co.jp</url>
</link>
<link>
<title>ONGS</title>
<url>http://www.ongs.gr.jp</url>
</link>
</bookmark>
<xsl:template match="bookmark">
<table border="1">
<xsl:for-each select="link">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="url"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template match="bookmark">
<table border="1">
<xsl:apply-templates select="link"/>
</table>
</xsl:template>
<xsl:template match="link">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="url"/></td>
</tr>
</xsl:template>
<xsl:template match="bookmark">
<table border="1">
<tr>
<xsl:for-each select="link/title">
<td><xsl:value-of select="."/></td>
</xsl:for-each>
</tr>
<tr>
<xsl:for-each select="link/url">
<td><xsl:value-of select="."/></td>
</xsl:for-each>
</tr>
</table>
</xsl:template>
7 条件处理
在XSLT中,进行条件处理的元素有xsl:if和xsl:choose两个。xsl:if进行“如果~就~”的if-then型处理,xsl:choose当有多个选择项存在时进行处理。
7.1 xsl:if
xsl:if元素拥有test属性,可指定逻辑表达式。逻辑表达式为真的情况下执行模板的转换,假的情况下不进行转换。
<xsl:if
test = "逻辑表达式">
<!-- 内容: 模板 -->
</xsl:if>
例如,对以下XML文档,
<people>
<person>
<name>张三</name>
<age>30</age>
</person>
<person>
<name>李四</name>
<age>20</age>
</person>
<person>
<name>周五</name>
<age>10</age>
</person>
</people>
<xsl:template match="people">
<ul>
<xsl:apply-templates select="person"/>
</ul>
</xsl:template>
<xsl:template match="person">
<li>
<xsl:value-of select="name"/>今年
<xsl:value-of select="age"/>岁
<xsl:if test="age[.< 18]">
(未成年)
</xsl:if>
</li>
</xsl:template>
<ul>
<li>张三今年30岁</li>
<li>李四今年20岁</li>
<li>周五今年10岁(未成年)</li>
</ul>
7.2 xsl:choose
xsl:choose元素由一个或一个以上的xsl:when元素和可选的xsl:otherwise元素组成。
<xsl:choose>
<xsl:when test="逻辑表达式">
<!-- 内容: 模板 -->
</xsl:when>
<xsl:otherwise>
<!-- 内容: 模板 -->
</xsl:otherwise>
</xsl:choose>
与xsl:if元素相同,test属性的逻辑表达式为真的情况下执行指定的模板。从上到下依次测试xsl:when元素,但只有第一个逻辑表达式为真的xsl:when院所的模板会被执行。如果所有的xsl:when元素的逻辑表达式均为假,则执行xsl:otherwise元素所指定的模板。xsl:otherwise元素不存在时则不执行任何模板。
例如,对于如下文档,
<ol>
<li>起床</li>
<li>洗脸</li>
<li>吃早饭</li>
<li>上班</li>
</ol>
<xsl:template match="ol">
<morning>
<xsl:for-each select="li">
<todo>
<xsl:choose>
<xsl:when test="position()=1">
<xsl:text>首先</xsl:text>
</xsl:when>
<xsl:when test="position()=last()">
<xsl:text>最后</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>第</xsl:text>
<xsl:value-of select="position()"/>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="."/>
</todo>
</xsl:for-each>
</morning>
</xsl:template>
<morning>
<todo>首先起床</todo>
<todo>第2洗脸</todo>
<todo>第3吃早饭</todo>
<todo>最后上班</todo>
</morning>
8 排序
为将数据排序,可以在xsl:apply-templates元素或xsl:for-each元素的子节点中增加xsl:sort元素。xsl:sort元素的基本代码如下所示。
<xsl:sort
select = "字符串表达式"
data-type = "数据类型"
order = "顺序"/>
例如,将如下所示的由姓名(name)、年龄(age)组成的人(person)的数据按照姓名和年龄进行排序。
<people>
<person>
<name>张三</name>
<age>30</age>
</person>
<person>
<name>李四</name>
<age>20</age>
</person>
<person>
<name>周五</name>
<age>10</age>
</person>
</people>
<xsl:template match="people">
<h1>按照年龄升序排列</h1>
<table border="1">
<xsl:apply-templates select="person">
<xsl:sort select="age"
data-type="number"/>
</xsl:apply-templates>
</table>
<h1>按照姓名降序排列</h1>
<table border="1">
<xsl:apply-templates select="person">
<xsl:sort select="name" data-type="text" order="descending"/>
</xsl:apply-templates>
</table>
</xsl:template>
<xsl:template match="person">
<tr>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="age"/></td>
</tr>
</xsl:template>
<xsl:template match="people">
<h1>按照年龄降序排列</h1>
<table border="1">
<xsl:for-each select="person">
<xsl:sort select="age" data-type="number"/>
<tr>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="age"/></td>
</tr>
</xsl:for-each>
</table>
<h1>排序</h1>
<table border="1">
<xsl:for-each select="person">
<xsl:sort select="name" data-type="text" order="descending"/>
<tr>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="age"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
9 变量
在XSLT中可以使用变量。
9.1 绑定变量
可以为变量赋值的元素有xsl:variable元素和xsl:param元素。
9.1.1 xsl:variable元素
基本代码如下所示。
<xsl:variable
name = "变量名"
select = "表达式">
<!-- 内容: 模板 -->
</xsl:variable>
<xsl:variable name="x"/>
<xsl:variable name="x" select="''"/>
<xsl:variable name="x" select="2"/>
<xsl:value-of select="item[$x]"/>
通过内容来赋值时,变量的值不是数值,而是结果树的片断。关于结果树的片断请参照参考文献[1]。
<xsl:variable name="x">2</xsl:variable>
<xsl:value-of select="item[$x]"/>
<xsl:value-of select="item[position()=$x]"/>
9.1.2 xsl:param元素
xsl:variable元素和xsl:param元素基本上相同。一个区别是,XML解释器假定xsl:param变量中保存着默认值,并可以使用xsl:with-param元素来向模板中传值。详细情况请参见参考文献[1]。
9.2 访问变量
在表达式中访问变量时,需要在变量名前加上$符号。使用xsl:value-of元素访问变量则可以输出变量的值。
10 指定输出格式
XSLT处理器的任务是从XML文档转换成新的XML文档,但是也能够输出XML文档之外的格式。通过xsl:output元素可以指定输出格式。该元素只能作为顶层元素使用。基本代码如下所示。
<xsl:output
method = "输出格式"
version = "版本"
encoding = "编码"
omit-xml-declaration = "yes" | "no"
standalone = "yes" | "no"
indent = "yes" | "no" />
- 结果树的根节点的元素名为html(不区分大小写),并且含有子元素。
- 不包含命名空间的URI。
- 结果树的根节点的子元素之前出现的文本只包含空白。
例如,输出i-mode能够浏览的HTML时,可以如下书写代码。由于i-mode只能使用Shift_JIS编码,因此需要按下面的方式定义。
<xsl:output method="html" encoding="Shift_JIS" indent="no"/>