经过了上一次的学习,我们学到了用于查询的XPATH,它会在之后的很多地方予以应用。

比如今天我们要讲的XSLT

第五章 XSLT

本章内容的学习目的:

  1. 理解和掌握XML文档的转换过程。
  2. 学习如何使用XSLT中的模板实现XML文档的转换。
  3. 学习如何使用XSLT中的排序、循环、条件分支语句实现各种处理。
  4. 创建使用参数和变量的XSLT文档。

5.1 XSLT

Q1:什么是XSLT?

C1:XSLT(eXtensible Stylesheet Language Transformations),其中的T表示“转换”,它是一种专门用于对XML树型数据进行结构重组转换的有力工具,可以根据指定的转换规则(在XSLT中称之为模板)将一个XML文档树或者其中的部分内容转换为另一种文档树形式。

XSLT不仅局限于结构上的重组,它可以将XML文档转换为任何形式,包括XML、HTML和普通文本。

XSL是它的前身,本来包含了XML数据表达和数据转换。数据转换部分从中独立出来成为XSLT。剩下的数据表达部分成为XSL-FO

Q2:XSLT有什么注意事项?

C2:

  1. XSLT语言是一种声明性的语言,即XSLT程序本身只是包含了一些转换规则的XML文档。
  2. XSLT处理程序(或称之为执行引擎)将首先确定 XSLT规则,然后根据规则的匹配条件通过XPath 表达式指定)、以及优先顺序完成相应的转换操作。
  3. XSLT本身也是一个XML文档,所以它必须严格遵守XML规范。其根元素的命名空间为http://www.w3.org/1999/XSL/Transform 即:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

5.1.1 XSLT的工作原理

Q3:XSLT是怎样工作的?

E3:

C3:

  1. 在进行XSLT的转换任务时,需要两个输入文档,一个是包含源数据的XML文档,一个是包含转换任务规则的XSLT文档
  2. 由XML解析器对这两个文档进行解析,将XML文档转换为所对应的文档树结构,将xslt(xsl)文档看作是一系列的转换规则
  3. 由XSLT引擎调用这些规则,对文档树进行遍历,分别处理其中指定的数据节点,将其转换为所需的结果集,并序列化为结果文档。

5.1.2 XSLT的一个简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="hello.xlst"?>
<message>Hello!</message> <!--这是hello.xsl文档-->

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- transform the input root (/) -->
<xsl:template match="/">
<html>
<body>
<h1>
<xsl:value-of select="message"/>
</h1>
</body>
</html>
</xsl:template>
</xsl:stylesheet> <!--这是hello.xslt文档-->

5.2 XSLT中模板的创建以及内置模板的使用

5.2.1 XSLT中模板的声明

Q4:怎么声明模板?

E4:

C4:模板标记之间的内容相当于一个函数的函数体,表示在调用该模板时应该执行的具体操作。而元素开始标记中的属性match、name、mode和priority则用于描述该模板的相关信息。

Q5:模板有哪些属性?

C5:

  1. 匹配路径属性 match

在XSLT中,模板的调用分为两种方式:

●根据模板的匹配路径(在遍历的过程中)调用,具体又有两种情况:

i. 对于模板 xsl:template match=”/“,XSLT 处理器在碰到 XML 文档的文档节点时自动调用该模板,就好像作为程序执行的入口

ii. 对于其他的模板match=other-pattern,将在模板xsl:template match=”/“的转换规则(函数体)中通过指出匹配路径的方式(使用xsl:apply-templates)进行隐式地或者显式地调用。

根据模板名称属性,使用 xsl:call-template name=template-name 进行调用。

  1. 名称属性 name

模板都没有具体的名称,因此将其称为无名模板。这些模板之所以没有名称,是因为它们的调用是在遍历文档树的过程中自动进行的,不需要名称。

也可以使用name属性为模板指定一个名称,使其成为命名模板 。例

1
2
3
4
5
6
7
8
9
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/" name="one"> <!--命名模板 one-->
<xsl:call-template name="another"/>
</xsl:template>

<xsl:template name="another"> <!--命名模板 another-->
<output>Simple output</output>
</xsl:template>
</xsl:stylesheet>

XSLT 处理器将调用命名模板 one(实际上名称 one 没有作用,因为是自动调用的),然后在模板 one 中使用 xsl:call-template 直接调用名为 another 的模板,在模板 another 执行结束之后,继续执行模板 one 的后续指令,这个过程非常类似于函数的调用

  1. 模式属性 mode

  1. 优先级属性 priority

注意:XSLT中允许存在同名的模板

当没有priority属性时,将执行后声明的模板

有priority属性时,将使用 priority取值较大的模板。

  1. 返回类型属性 as

模板返回类型属性as的取值,表示该模板应该返回的数据类型

注:该返回的XML元素包含了子元素和文本内容

模板的返回类型属性as是一个可选的参数,如果不指定该参数,则模块可以输出任意的文本内容

5.2.1 XSLT中模板的调用

1.使用xsl:apply-templates 调用模板

调用顺序:在广度优先、逐层向下的遍历过程中,指定继续遍历当前节点的所有子节点

完整语法:

1
2
3
4
<xsl:apply-templates select=Expression mode=QName>
......
</xsl:apply-templates>

对于xsl:apply-templates元素,select和mode属性都是可选的,即可以直接使用<xsl:apply-templates/>

对于上例,XSLT自动调用模板1,开始对整个XML文档树的遍历,即依次调用“处理指令”、“message元素”所对应的模板。所以模板1中使用了<xsl:apply-templates/>,表示调用当前节点的所有子节点所对应的模板。若模板1中无<xsl:apply-templates/>遍历过程将中断,不会处理除文档节点”/“之外的任何节点。

下面介绍该元素的两个属性

  1. select(取值为一个XPath表达式)允许指定仅遍历当前节点的哪些子节点(以调用相应的模板)

例:<xsl:apply-templates select="message"/>仅调用“message元素”所对应的模板

  1. mode属性指定需要在match属性取值相同的模板中,选择哪一个进行调用。

2.使用xsl:call-template 调用模板

在xsl:template元素开始标记和结束标记之间,使用 xsl:param元素为所在的模板声明参数,其as属性可以指定参数的数据类型。 例

注:用于为模板声明参数的xsl:param元素必须出现在模板正文的前面

​ 在模板正文中,使用$+’模板参数名’来引用模板参数,以避免将其作为字符串进行处理。

5.2.2 XSLT中的内置模板

Q6:怎样分析内置模板?

E6:

C6:模板1’的match=“*|/”是用于处理文档节点和所有元素节点(使用了通配符*),该模板的任务是调用所有子节点的处理模板。

​ 模板2‘的match=“text()|@*”,表示它用于处理文本节点和所有属性节点(使用了通配符@),由于文本节点和属性节点不可能再包含子节点,所以这个模板的任务是直接打印出当前节点(文本节点和属性节点)的内容(*)

Q7:内置模板有什么作用?

C7:内置模板中仅对文本节点和属性节点进行了处理,即输出了其文本内容。其作用在于:让我们集中精力编写相关节点的处理模板,而无需过多地操心整个遍历过程中模板的逐层调用。只有在用户没有自定义处理某个节点的模块规则时,才会调用内置模块中的规则;否则,用户自定义的模块规则将覆盖内置模块中的规则

Q8:内置模板存在什么问题?

E8:

C8:在Company模板中,<xsl:apply-templates/>将调用其子节点(Name和Person)的处理模板。但这时不存在match=“Name”的模板(修改错误时则只需要加上空模板Name即可),于是调用默认模板(先1后2),输出了文本节点的内容,导致文本节点的内容输出了两次,而Person的子元素Name没有出现两次。

5.3 XSLT模板中各种转换功能的实现

5.3.1 value-of text

Q9:如何使用value-of?

E9:

1
2
3
4
<message>
<subject>I</subject><predicate>Love</predicate><object>You</object>
</message>
<xsl:value-of select="."/> <!--会输出ILoveYou-->

C9:对于文本节点和属性节点,xsl:value-of select=“.” 当然提取的是文本节点的内容和属性节点的取值;对元素节点使用 ,那么将得到元素节点的 String-Value,如上例。

Q10:如何输出空格?

E10:

C10:此时应该使用<xsl:text><xsl:text/>。xsl:text元素开始标记和结束标记之间的内容会原封不动的输出到结果中,但是其中不能包含其他XML的标记。

5.3.2 xsl:for-each xsl:sort

Q11:怎么使用for-each与sort

E11:

C11:例中选择出了所有person的子元素Name,并将它们进行排序,随后进行输出。

关于sort的语法:

1
2
3
4
<xsl:sort select=expression 
data-type={"text"|"number"|QName}
order={"ascending"|"descending"}
case-order={"upper-first"|"lower-first"}/>

5.3.3 xsl:if

Q12:怎么使用if?

E12:

C12:if通常与for-each搭配,根据条件输出所需要的文本或节点。

5.3.4 xsl:choose xsl:when xsl:otherwise

E13:

5.3.5 在输出中创建XML节点

Q13:如何使用copy和copy-of

E13:

C13:在上例中,递归的使用了xsl:copy,用于复制每一个节点到结果树中。而xsl:copy-of直接在文档入口处复制了根元素和下面的整棵树(包括属性和子元素)到结果树中,二者存在以下差异:

缺点:xsl:copy 和 xsl:copy-of 可以将当前节点从源复制到输出,但是不能根据需要生成任意的 XML 节点

Q14:如何使用element

E14:

C14:上例通过xsl:element,将源文档中的message元素改名为了new-message

Q15:如何使用attribute

E15:

C15:上例先通过copy复制了一个空的message元素,随后为其添加了属性content,并将原来message的文本内容赋给了content。

Q16:如何使用attribute-set?

E16:

5.3.6 如何控制输出格式?

E17:<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

C17:这表示输出为XML格式,版本为XML1.0,编码方式为UTF-8并且进行缩进如果不进行缩进,输出的会写在同一行,难以体现出层次关系。

通过本次学习,我们学习到了XSLT的基本语法和其转换方式,在考试中XSLT常考于大题中,即给出原来的XML和XSLT让你写出转换后的XML文档。需要仔细学习规则,不然会失分很多。