XSLT 指 XSL 转换(XSL Transformations),这是一篇XSLT的教程,

概述

XSLT 用于将一种 XML 文档转换为另外一种 XML 文档,或者可被浏览器识别的其他类型的文档,比如 HTML 和 XHTML。通常,XSLT 是通过把每个 XML 元素转换为 (X)HTML 元素来完成这项工作的。

把文档声明为 XSL 样式表的根元素是 <xsl:stylesheet><xsl:transform>。这两个是同义的,均可使用。

正确的样式表声明如下:

1
2
3
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
</xsl:stylesheet>
1
2
3
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
</xsl:transform>

而在XML文档中引用XSLT需要在文件头加一行声明:<?xml-stylesheet type="text/xsl" href="XSLT路径"?>

基本语法

template

模板就类似于编程语言中的函数,我们可以直接在其中编写XSLT语法,也可以在其他地方调用它。写在模板中的内容会以样式表的形式被显示出来,其基本框架为<xsl:template match="xpath"></xsl:template> match 属性中的值为一个xpath路径,用于在XML文档中匹配要被显示的节点。举个例子:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/book">
<html>
<body>
<h2>My books</h2>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

这个模板指向了book节点,但由于我们还没学习如何调用节点中的值,所以只简单地输出了一些HTML格式,在输出的格式中会以

1
2
3
4
5
<html>
<body>
<h2>My books</h2>
</body>
</html>

的HTML样式输出。

调用模板

在创建模板的时候,可以为其添加一个name属性作为名字:<xsl:template match="xpath" name="name"> ,然后在其他位置就可以用<xsl:call-template name="template-name">对其进行调用了,类似于函数调用的过程。

其他属性

  1. mode :用于区分同名模板,进行进一步标识
  2. priority :表示模板的优先级。当没有priority属性时,将执行后声明 的模板;有priority属性时,将使用 priority 取值较大 的模板。
  3. as :表示该模板应该返回的数据类型。如 <xsl:template match="book" as="element()"> 表示该模板将会返回一个XML元素作为结果。as是一个可选的参数,如果不指定该参数,则模块可以输出任意的文本内容。

apply-templates

<xsl:apply-templates select="xpath" mode="mode-name"/> 元素用来从当前节点开始对子结点中符合xpath表达式的节点中BFS向下遍历当前模板,其中mode属性可以用来区分不同元素。听起来可能很乱,没事,我也没觉得我能讲清楚。但是看完以下例子就能明白了:

以下例子都建立在如下XML文档中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book>
<title>Harry Potter</title>
<price>20</price>
</book>
<book>
<title>C++</title>
<price>35</price>
<author>
<name>thq</name>
<name>THU</name>
</author>
</book>
</books>

对其调用如下XSLT:

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="books">
<h3><xsl:apply-templates/></h3>
</xsl:template>
</xsl:stylesheet>

输出结果为 Harry Potter20C++35thqTHU ,这是因为我们没有限定select属性的值,默认将所有子节点都加入h3标签当中了。

倘若对其调用这个XSLT:

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="books">
<h3><xsl:apply-templates select="book/title"/></h3>
</xsl:template>
</xsl:stylesheet>

应该容易猜到,输出结果为 Harry PotterC++

param

<xsl:param name="param-name"/> 元素可以用来声明模板中的形参,而 <xsl:with-param name="param-name" select="xpath"/> 元素可以在调用模板的时候将xpath所指向的元素作为模板参数传入。在如下XML文档中:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book>
<title>Harry Potter</title>
<price>20</price>
</book>
</books>

调用如下XSLT样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="books">
<xsl:call-template name="temp">
<!-- 注意这里我传的是price的值 -->
<xsl:with-param name="title" select="book/price"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="temp">
<xsl:param name="title"/>
<h3><xsl:value-of select="$title"/></h3>
</xsl:template>
</xsl:stylesheet>

最终输出结果为 <h3>20</h3>

output

在XSLT中如果要规定输出时的样式,可以通过一行 <xsl:output/> 声明来实现,其属性值有以下几种可选取值:

属性 描述
method xml html text name 定义输出的格式。默认是 XML(但是如果根节点的第一个子节点是 <html>,且在这之前没有文本节点,则默认是 HTML)。
version string 设置输出格式的 W3C 版本号。(仅在 method=“html” or method=“xml” 时使用)。
encoding string 设置输出中编码属性的值。
omit-xml-declaration yes no “yes” 规定在输出中省略 XML 声明(<?xml...?>)。默认是 “no”。
standalone yes no “yes” 规定 XSLT 处理器应输出独立文档声明。默认是 “no”。
doctype-public string 规定 DTD 中要使用的公共标识符。即输出中 DOCTYPE 声明的 PUBLIC 属性的值。
doctype-system string 规定 DTD 中要使用的系统标识符。即输出中 DOCTYPE 声明的 SYSTEM 属性的值。
cdata-section-elements namelist 一个空格分隔的元素列表,这些元素的文本内容应作为 CDATA 部分来输出。
indent yes no “yes” 规定输出应根据其层次结构进行缩排。
media-type string 定义输出的 MIME 类型(数据的媒体类型)。默认是 “text/xml”。

value-of

<xsl:value-of select="xpath"/> 元素用于提取某个 XML 元素的值,并把值添加到转换的输出流中。select属性中的xpath值即为从模板匹配的节点开始 匹配的节点,举个例子:对于XML文档

1
2
3
<book>
<title>Harry Potter</title>
</book>

调用以下XLST模板:

1
2
3
4
5
6
7
<xsl:template match="/book">
<html>
<body>
<h2><xsl:value-of select="title"/></h2>
</body>
</html>
</xsl:template>

会得到一行以HTML的h2格式输出的Harry Potter

for-each

<xsl:for-each select="xpath"> 的用处很显然了,就是选取匹配的节点组中的每个元素,比如对于XML文档

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book>
<title>Harry Potter</title>
<price>20</price>
</book>
<book>
<title>TanHaoQiangC++</title>
<price>35</price>
</book>
</books>

调用以下XSLT样式:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/books">
<xsl:for-each select="book">
<h2><xsl:value-of select="title"/></h2>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

分别 输出h2格式的Harry Potter与TanHaoQiangC++。

sort

可以在for-each中添加一行 <xsl:sort select="xpath"/> 元素来对xpath匹配到的节点进行排序。

if

<xsl:if test="expression"> 的作用与编程语言中的if相同,用于筛选出符合expression表达式的项,对于上面那个XML文档的例子,如果调用以下XSLT样式:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/books">
<xsl:for-each select="book">
<xsl:if test="price < 30">
<h2><xsl:value-of select="title"/></h2>
</xsl:if>>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

则会筛选出price元素的值小于30的book,并输出其标题,即:Harry Potter。

choose

<xsl:choose> 元素用于结合 <xsl:when test="expression"><xsl:otherwise> 来表达多重条件测试。类似于 if-else 语句的作用,不多赘述。

processing-instruction与comment

分别用来生成处理指令与注释。举个例子:

1
2
3
4
5
6
<xsl:comment>
<xsl:text>Processing instruction should appear next</xsl:text>
</xsl:comment>
<xsl:processing-instruction name="xml-stylesheet">
<xsl:text>type="text/xsl" href="some.xslt"</xsl:text>
</xsl:processing-instruction>
1
2
3
输出
<!--Processing instruction should appear next-->
<?xml-stylesheet type="text/xsl" href="some.xslt"?>

variable

<xsl:variable name="var-name"> 元素可以用来定义一个变量,在后续内容中可以重复调用:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="var">
<table><tr>title</tr><tr>author</tr></table>
</xsl:variable>
<xsl:template match="book">
<html>
<xsl:copy-of select="$var"/>
</html>
</xsl:template>
</xsl:stylesheet>

其输出为

1
2
title 
author

输出XML节点

如果要在XLST的输出中输出动态的XML节点(根据原XML文档内容动态变化),则不能用以上简单的方式输出,以下介绍几种动态生成XML节点的方式:

copy与copy-of

这两个元素的区别在于 <xsl:copy/> 元素可以输出当前节点的无子节点与属性版本,而 <xsl:copy-of select="xpath"/> 可以输出xpath所指向的节点的完整副本,下面举两个例子:对于该XML文档:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book>
<title>Harry Potter</title>
<price>20</price>
</book>
</books>

调用copy节点的XSLT:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="book">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>

输出为:

1
2
<?xml version="1.0" encoding="UTF-8"?>
<book/>

调用以下含有copy-of元素的XSLT:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="book">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>

输出结果为:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<book>
<title>Harry Potter</title>
<price>20</price>
</book>

element

copy与copy-of可以用来输出原文档中已有的XML节点,但无法生成新节点,若要实现后者,则需要使用 <xsl:element name="element-name"></xsl:element> 元素。没什么好说的,应该看了就知道怎么用吧!

attribute与attribute-set

顾名思义,这个是用来为元素添加属性的,具体用法为 <xsl:attribute name="attribute-name">attribute-value</xsl:attribute>,就用上方那个XML原文档举个例子吧,比如用这个XSLT样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="book">
<xsl:element name="BOOKS">
<xsl:copy>
<xsl:attribute name="price">
<xsl:value-of select="price"/>
</xsl:attribute>
</xsl:copy>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

其输出即为:

1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<BOOKS>
<book price="20"/>
</BOOKS>

<xsl:attribute-set name="set-name"> 则可以将多个属性值封装起来,提高其复用性,可以在要使用的地方通过 use-attribute-sets="set-name" 的属性进行调用。比如以下这个XSLT样式,可以实现与上方那个相同的效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="book">
<xsl:element name="BOOKS">
<xsl:copy use-attribute-sets="set"/>
</xsl:element>
</xsl:template>
<xsl:attribute-set name="set">
<xsl:attribute name="price">
<xsl:value-of select="price"/>
</xsl:attribute>
</xsl:attribute-set>
</xsl:stylesheet>

XSLT中的其他机制

模块化

<xsl:include href="URI"><xsl:import href="URI"/> 元素都可以用来导入其他XSLT样式。其唯一的区别在于:import 导入的样式表的优先级低于 导出的样式表,也就意味着可以在当前样式表中重写元素。而include 导入的样式表拥有与包含的样式表(including style sheet)相同 的优先级。

多文档

使用 document('URI') 函数可以读取另一个XML文档中的节点到当前XML中使用,比如: <xsl:for-each select="document('books.xml')/books">

索引

使用 <xsl:key name="key-name" match="pattern" use="expression"/>key('key-name', 'value') 函数可以实现索引。

其中 key 元素中的 match 属性定义该键被应用到哪个节点,而 use 属性指定要作为每个节点的键的值使用的表达式。

比如对于XML文档:

1
2
3
4
5
<persons>
<person name="Tarzan" id="050676"/>
<person name="Donald" id="070754"/>
<person name="Dolly" id="231256"/>
</persons>

定义一个索引: <xsl:key name="preg" match="person" use="@id"/> ,如果要找到所有 id=“050676” 的 person ,可以使用 <xsl:for-each select="key('preg','050676')">

提示

<xsl:key> 是顶级元素。

不能使用参数或变量引用作为key匹配的一部分。

只有在调用key()时,XSLT处理器才会创建相应的索引。

一个节点上可以声明多个键。

学了一天终于学完了我困了886。