XML - 管理数据交换/XSLT 和样式表
XML - 管理数据交换
|
相关主题
|
参与
|
上一章 | 下一章 |
← CSS | Cocoon → |
学习目标
|
在前面的章节中,我们介绍了使用 XSL 样式表将 XML 文档转换为 HTML 的基础知识。本章将简要回顾这些概念,并介绍许多新概念。它是创建样式表的参考。
XML 样式表
[edit | edit source]可扩展样式表语言 (XSL) 提供了一种方法来转换和格式化 XML 文档的内容以供显示。它包括两个部分,XSL 转换 (XSLT) 用于转换 XML 文档,以及 XSLFO (XSL 格式化对象) 用于格式化或将样式应用于 XML 文档。XSL 转换语言 (XSLT) 用于将 XML 文档从一种形式转换为另一种形式,包括新的 XML 文档、HTML、XHTML 和文本文档。XSL-FO 可以从 XML 创建 PDF 文档以及其他输出格式。使用 XSLT,您可以有效地回收内容,重新设计它以用于新文档,或更改它以适应无限的用途。例如,从单个 XML 源文件,您可以提取一个适合打印的文档,一个适合 Web 的文档,一个适合 Unix 手册页面的文档,以及另一个适合在线帮助系统的文档。您还可以选择从存储多种语言文本的 XML 源中提取以特定语言编写的文档的特定部分。可能性是无限的!
XSLT 样式表是一个 XML 文档,包含完整的元素和属性。它有两种类型的元素,顶层元素和指令元素。顶层元素直接位于 stylesheet
根元素之下。指令元素表示一组格式化指令,这些指令规定如何转换 XML 文档的内容。在转换过程中,XSLT 会分析 XML 文档或源树,并将其转换为节点树,这是整个 XML 文档的分层表示,也称为结果树。每个节点代表 XML 文档的一部分,例如元素、属性或一些文本内容。XSL 样式表包含预定义的“模板”,这些模板包含有关如何处理节点的指令。XSLT 将使用 match
属性将 XML 元素节点与模板相关联,并将它们转换为结果文档。
让我们回顾一下第二章中的样式表 city.xsl,并更详细地检查它。
图 1:城市实体的 XML 样式表
<?xml version="1.0" encoding="UTF-8"?>
<!--
Document: city.xsl
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head>
<title>Cities</title>
</head>
<body>
<h2>Cities</h2>
<xsl:apply-templates select="cities"/>
</body>
</html>
</xsl:template>
<xsl:template match="cities">
<!-- the for-each element can be used to loop through each node in a specified node set (in this case city) -->
<xsl:for-each select="city">
<xsl:text>City: </xsl:text>
<xsl:value-of select="cityName"/>
<br/>
<xsl:text>Population: </xsl:text>
<xsl:value-of select="cityPop"/>
<br/>
<xsl:text>Country: </xsl:text>
<xsl:value-of select="cityCountry"/>
<br/>
<br/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
- 由于样式表是一个 XML 文档,因此它以 XML 声明开头。这包括伪属性
encoding
和standalone
。它们被称为伪属性,因为它们与元素属性不同。standalone 属性允许您直接指定外部 DTD <xsl:stylesheet>
标签声明样式表的开始,并标识版本号和官方 W3C 命名空间。注意 XSLT 命名空间的常规前缀 xsl。声明前缀后,必须将其用于所有元素。<xsl:output>
标签是一个可选元素,它决定如何输出结果树。<xsl:template>
元素定义模板的开始,并包含在匹配指定节点时应用的规则。match
属性用于将模板与 XML 元素(在本例中为 XML 源文档的根 (/) 或整个分支)相关联(匹配)。- 如果未指定输出方法,则输出将默认设置为 HTML,在本例中,因为根元素是
<html>
开始标签。 apply-templates
元素是一个空元素,因为它没有字符内容。它将模板规则应用于当前元素或元素的子节点。select
属性包含一个位置路径,告诉它处理哪个元素的内容。- 指令元素
value-of
提取所选节点子节点的字符串值,在本例中为cityName
的文本节点子节点。
template
元素定义执行更改的规则。这可以是任何数量的事情,包括简单的纯文本转换、添加或删除 XML 元素,或者只是转换为 HTML,当模式匹配时。在元素的 match
属性中定义的模式包含一个简化的 XPath 位置路径。这基本上是文档中根元素的名称,在本例中为“tourGuide”。
在将 XML 文档转换为 HTML 时,处理器期望样式表中的元素格式良好,就像 XML 一样。这意味着所有元素都必须有结束标签。例如,看到单独的 <p>
标签并不罕见。XSLT 处理器要求具有开始标签的元素必须以结束标签关闭。对于 <br>
元素,这意味着使用 <br></br>
或 <br />。
如第三章所述,br
元素是一个空元素。这意味着它在标签之间不包含任何内容,但它可能具有属性。虽然 HTML 输出不会输出任何结束标签,但它们在样式表中仍然必须具有结束标签。例如,在样式表中,您将列出:<img src="picture.jpg"></img>
或作为空元素 <img src="picture.jpg" />
。HTML 输出将删除结束标签,使其看起来像这样:<img src="picture.jpg">
另外,处理器将识别 html 标签,无论它们采用什么大小写 - BODY、body、Body 都被解释为相同。
输出
[edit | edit source]XSLT 可用于将 XML 源转换为许多不同类型的文档。XHTML 也是 XML,如果它格式良好,那么它也可以用作源或结果。但是,将纯 HTML 转换为 XML 不会起作用,除非它首先被转换为 XHTML 以符合 XML 1.0 建议。以下是 XSLT 执行的所有可能的类型到类型转换的列表。
图 2:类型到类型转换
XML | XHTML | HTML | 文本 | |
XML | X | X | X | X |
XHTML | X | X | X | X |
HTML | ||||
文本 |
样式表中的 output
元素决定如何输出结果树。此元素是可选的,但它允许您更好地控制输出。如果您不包含它,输出方法将默认为 XML,或者如果结果树中的第一个元素是 <html>
元素,则默认为 HTML 输出。图 3 列出了属性。
图 3:元素输出属性 (来自 Wiley:Michael Fitzgerald 编著的 XSL Essentials)
属性 | 描述 |
cdata-section-elements | 指定包含结果树中 CDATA 部分的空格分隔的元素名称列表。 CDATA 转义通常被解释为标记的字符,例如 < 或 &。 |
doctype-public | 将公共标识符放置在结果树中文档类型声明中。 |
doctype-system | 将公共标识符放置在结果树中文档类型声明中。 |
encoding | 设置首选编码类型,例如 UTF-8、ISO-8859 等。这些值不区分大小写。 |
indent | 指示 XSLT 处理器可以缩进结果树中的内容。可能的值是
|
media-type | 设置结果树内容的媒体类型(MIME 类型)。 |
method | 指定输出类型。合法值为 xml、html、text 或其他限定名称。 |
omit-xml-declaration | 告诉 XSLT 处理器是否包含 XML 声明。 |
standalone | 告诉 XSLT 处理器在 XML 声明中包含伪属性(如果未省略),其值为 "yes" 或 "no" 。这指示文档是否依赖于外部标记声明,例如外部 DTD 中的声明。 |
version | 设置输出方法的版本号,例如用于输出的 XML 版本(默认值为 1.0 )。 |
XML 到 XML
[edit | edit source]由于我们已经对将 XML 文档转换为 HTML 进行了大量的练习,因此我们将使用第二章中使用的 city.xml,使用 host.xsd 作为模式将其转换为另一个 XML 文件。
图 4:城市实体的 XML 文档
<?xml version="1.0" encoding="UTF-8"?>
<!--
Document: city.xml
-->
<cities xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:noNamespaceSchemaLocation='host.xsd'>
<city>
<cityID>c1</cityID>
<cityName>Atlanta</cityName>
<cityCountry>USA</cityCountry>
<cityPop>4000000</cityPop>
<cityHostYr>1996</cityHostYr>
</city>
<city>
<cityID>c2</cityID>
<cityName>Sydney</cityName>
<cityCountry>Australia</cityCountry>
<cityPop>4000000</cityPop>
<cityHostYr>2000</cityHostYr>
<cityPreviousHost>c1</cityPreviousHost >
</city>
<city>
<cityID>c3</cityID>
<cityName>Athens</cityName>
<cityCountry>Greece</cityCountry>
<cityPop>3500000</cityPop>
<cityHostYr>2004</cityHostYr>
<cityPreviousHost>c2</cityPreviousHost >
</city>
</cities>
图 5:按城市 ID 列出城市的城市实体的 XSL 文档
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select="//city[count(cityPreviousHost) = 0]">
<br/><xsl:text>City Name: </xsl:text><xsl:value-of select="cityName"/><br/>
<xsl:text> Rank: </xsl:text><xsl:value-of select="cityID"/><br/>
<xsl:call-template name="output">
<xsl:with-param name="context" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="output">
<xsl:param name="context" select="."/>
<xsl:for-each select="//city[cityPreviousHost = $context/cityID]">
<br/><xsl:text>City Name: </xsl:text> <xsl:value-of select="cityName"/><br/>
<xsl:text> Rank: </xsl:text><xsl:value-of select="cityID"/><br/>
<xsl:call-template name="output">
<xsl:with-param name="context" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
图 6:主机城市实体的 XML 模式
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:element name="cities">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="city" type="cityType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="cityType">
<xsd:sequence>
<xsd:element name="cityID" type="xsd:ID"/>
<xsd:element name="cityName" type="xsd:string"/>
<xsd:element name="cityCountry" type="xsd:string"/>
<xsd:element name="cityPop" type="xsd:integer"/>
<xsd:element name="cityHostYr" type="xsd:integer"/>
<xsd:element name="cityPreviousHost" type="xsd:IDREF" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
图 7:城市实体的 XML 样式表
<?xml version="1.0" encoding="UTF-8"?>
<!--
Document: city2.xsl
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes" />
<xsl:attribute-set name="date">
<xsl:attribute name="year">2004</xsl:attribute>
<xsl:attribute name="month">03</xsl:attribute>
<xsl:attribute name="day">19</xsl:attribute>
</xsl:attribute-set>
<xsl:template match="tourGuide">
<xsl:processing-instruction name="xsl-stylesheet"> href="style.css" type="text/css"<br />
</xsl:processing-instruction>
<xsl:comment>This is a list of the cities we are visiting this week</xsl:comment>
<xsl:for-each select="city">
<!-- element name creates a new element where the value of the attribute name sets name of
the new element. Multiple attribute sets can be used in the same element -->
<!-- use-attribute-sets attribute adds all the attributes declared in attribute-set from above -->
<xsl:element name="cityList" use-attribute-sets="date">
<xsl:element name="city">
<xsl:attribute name="country">
<xsl:apply-templates select="country"/> </xsl:attribute>
<xsl:apply-templates select="cityName"/>
</xsl:element>
<xsl:element name="details">Will write up a one page report of the trip</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
- 虽然
output method
设置为“xml”,但由于结果树的根没有<html>
元素,因此它将默认输出为 XML。 attribute-set
是一个顶层元素,它通过名称“date”创建一组属性。这个属性集可以在整个样式表中重复使用。attribute-set
元素也具有 use-attribute-sets 属性,允许您将多个属性集链接在一起。processing-instruction
生成 XML 样式表处理指令。comment
元素在结果树中创建注释。attribute
元素允许您向结果树中创建的元素添加属性。
样式表生成此结果树
图 8:城市实体的 XML 结果树
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!--
Document: city2.xsl
-->
<?xsl-stylesheet href="style.css" type="text/css"?>
<!--This is a list of the cities we are visiting this week-->
<cityList year="2004" month="03" day="19">
<city country="Belize">Belmopan</city>
<details>Will write up a one page report of the trip</details>
</cityList>
<cityList year="2004" month="03" day="19">
<city country="Malaysia">Kuala Lumpur</city>
<details>Will write up a one page report of the trip</details>
</cityList>
</stylesheet>
处理器会在结果树的顶部自动插入 XML 声明。处理指令 (PI) 是供处理应用程序使用的指令。在本例中,href 指向本地样式表,该样式表将在处理 XML 文档时应用于该文档。我们使用 <xsl:element>
在结果树中创建新内容并向其添加属性。
还有另外两个指令元素用于将节点插入结果树。它们是 copy
和 copy-of
。与 apply-templates
不同,apply-templates
仅复制子节点的内容(如子文本节点),这些元素会复制所有内容。以下代码展示了如何使用 copy 元素复制 city.xml 中的 city 元素
图 9:Copy 元素
<xsl:template match="city">
<xsl:copy />
</xsl:template>
结果如下所示
图 10:Copy 元素结果
<?xml version="1.0" encoding="utf-8">
<city />
<city />
输出并不十分有趣,因为 copy 不会拾取子节点,而只会拾取当前节点。在我们的示例中,它拾取了 city.xml 文件中的两个 city 节点。copy 元素有一个可选属性 use-attribute-sets,它允许您向元素添加属性。但是,它会保留任何其他属性,除了命名空间(如果存在)。以下是如果在源文档中声明了命名空间(在本例中为默认命名空间)的结果
图 11:命名空间结果
<?xml version="1.0" encoding="utf-8">
<city xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<city xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
如果您想从源文件中复制不止一个节点,copy-of
元素将包含当前节点以及与其关联的任何属性节点。这包括可能存在的任何节点,例如命名空间节点、文本节点和子元素节点。当我们将 copy-of
元素应用于 city.xml 时,结果几乎是 city.xml 的精确副本!您还可以使用 <xsl:copy-of select="comment()"/>
和 <xsl:copy-of select="processing-instruction(name)"/>
复制注释和处理指令,其中 name
是您希望检索的处理指令中 name 属性的值。
您可能会问,这有什么用?有时您只想获取节点并进行操作!例如,如果您想将 city.xml 的副本放置到 SOAP 信封中,可以使用 copy-of
很容易地做到这一点。如果您还不知道,简单对象访问协议 (SOAP) 是用于打包 XML 文档以进行交换的协议。这在 B2B 环境中非常有用,因为它提供了一种打包 XML 消息的标准方法。您可以在 www.w3.org/tr/soap 上阅读有关 SOAP 的更多信息。
使用 XML 编辑器创建上述 XML 样式表,并尝试使用 copy
和 copy-of
元素。
模板
[edit | edit source]由于模板定义了更改节点的规则,因此有意义的是对它们进行重用,无论是在同一个样式表中还是在其他样式表中。这可以通过命名模板来实现,然后使用 call-template
元素来调用它。还可以包含来自其他样式表的命名模板。您会很快了解到这在实际应用中有多么有用。以下是一个使用命名模板的示例
图 110:命名模板
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" />
<xsl:template match=" /">
<xsl:call-template name="getCity" />
</xsl:template>
<xsl:template name="getCity">
<xsl:copy-of select="city" />
</xsl:template>
</xsl:stylesheet>
模板还具有 mode 属性。这允许您多次处理一个节点,每次产生不同的结果,具体取决于模板。让我们创建一个样式表来练习模式。
图 12:XML 模板模式
<?xml version="1.0" encoding="UTF-8"?>
<!--
Document: cityModes.xsl
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:template match="tourGuide">
<html>
<head>
<title>City - Using Modes</title>
</head>
<body>
<xsl:for-each select="city">
<xsl:apply-templates select="cityName" mode="title" />
<xsl:apply-templates select="cityName" mode="url" />
<br />
</xsl:for-each>
</body>
</html>
</xsl:template>
<xsl:template match="cityName" mode="title">
<h2><xsl:value-of select="current()"/></h2>
</xsl:template>
<xsl:template match="cityName" mode="message">
<p>Come visit <b><xsl:value-of select="current()" /></b>!</p>
</xsl:template>
</xsl:stylesheet>
apply-templates select="cityName" mode="title"
告诉处理器查找具有相同 mode 属性值的模板value-of select="current()"
返回当前节点,该节点使用value-of
转换为字符串。使用select="."
也会返回当前节点。
结果并不十分令人满意,因为我们没有对文件进行太多操作,但它能够说明问题。
图 13:来自上述样式表的结果
<h2>Belmopan</h2>
Come visit <b>Belmopan</b>!
<h2>Kuala Lumpur</h2>
Come visit <b>Kuala Lumpur</b>!
默认情况下,XSLT 处理器具有内置的模板规则。如果您应用一个没有匹配规则的样式表,并且它未能匹配模式,则会自动应用默认规则。默认规则输出所有元素的内容。
排序
[edit | edit source]编写“格式良好”的代码 XML 至关重要。但是,有时,仅仅显示信息(数据管理的最基本级别)并不是正确识别项目所必需的。作为信息技术专家,必须充分了解顺序对于解释至关重要。可以通过将数据放到易于读取的格式中来实现顺序。然后,这些信息就会变得易于使用。使用比较模型或简单地查找特定名称或项目变得非常容易。查找特定音乐艺术家、标题或音乐类型变得非常容易。作为信息专家,您必须充分意识到,通常需要对信息进行排序。XMLT 中排序的基础是 xsl:sort 命令。xsl:sort 元素体现了排序键组件。排序键组件标识如何为排序信息中的每个项目标识排序键值。排序键值定义为“使用第 N 个排序键组件为项目计算的值”。排序键组件的重要性通过其 select 属性或包含的序列构造器来实现。序列构造器定义为“样式表中零个或多个同级节点的序列,这些节点可以计算为返回节点和原子值的序列”。在某些情况下,两者都不存在。在这种情况下,默认值为 select=".",如果它是原子值,则对项目的实际值进行排序,如果它是节点,则对项目的类型值进行排序。如果存在 select 属性,则其值必须是 Xpath 表达式。
以下是 <xsl:sort> 元素用于对输出进行排序的方式。
排序信息按如下方式保存:在 XML 中对输出进行排序非常容易,方法是在 XSL 文件中的 <xsl:for-each> 元素之后添加 <xsl:sort> 元素。
图 14:包含排序功能的样式表
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>TourGuide Example</h2>
<xsl:apply-templates select="cities"/>
</body>
</html>
</xsl:template>
<xsl:template match="cities">
<xsl:for-each select="city">
<xsl:sort select="cityName"/>
<xsl:value-of select="cityName"/>
<xsl:value-of select="cityCountry"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
此示例将按艺术家名称对文件进行字母排序。注意:select 属性指示要排序的 XML 元素。可以按“标题”或“艺术家”选择和排序信息。这些是 XML 文档将在文件主体中显示的类别。
我们之前已使用 sort
函数对 if
语句的结果进行排序。sort 元素还有许多其他用途。它本质上指示处理器根据某些标准对节点进行排序,这称为排序键。它默认按升序对元素进行排序。以下是对 sort 使用的不同属性的简短列表
图 15:排序属性
属性 | 描述 |
select
|
指定要处理的节点 |
order
|
指定排序顺序:“ascending” 或 “descending” |
case-order
|
确定大写文本是否在小写文本之前排序:“upper-first” 或 "lower-first" |
data-type
|
默认按文本数据排序:“text”、“number” 或 QName(限定名) |
lang
|
指示所使用的语言,因为某些语言使用不同的字母表。“en”、“de”、“fr”等。如果未指定值,则从系统环境确定语言。 |
sort 元素可以在 apply-templates
或 for-each
元素中使用。它还可以在一个模板中或在多个模板中使用多次,以创建子排序级别。
编号
[edit | edit source]number
指令元素允许您在结果中插入数字。结合 sort 元素,您可以轻松地创建编号列表。当将此简单的样式表 hotelNumbering.xsl 应用于 city_hotel.xml 时,我们将获得下面列出的结果
图 16:排序和编号列表
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Document: hotelNumbering.xsl
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="tourGuide/city/hotel">
<xsl:sort/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="hotel">
<xsl:number value="position()" format="
 0. "/>
<xsl:value-of select="hotelName"/>
</xsl:template>
</xsl:stylesheet>
图 17:hotelNumbering.xsl 结果
1. Bull Frog Inn 2. Mandarin Oriental Kuala Lumpur 3. Pan Pacific Kuala Lumpur 4. Pook's Hill Lodge
value
中的表达式将被计算,而 position()
的值将基于排序后的节点列表。为了改善外观,我们添加了 format 属性,其中包含一个换行符字符引用 (
)、一个零位数字(表示数字将是零位数字,表示该数字将是整数类型)以及一个句号和一个空格,以使它看起来更美观。格式列表可以基于以下序列
图 17:编号格式
format=" A. "
– Uppercase lettersformat=" a. "
– Lowercase lettersformat=" I. "
– Uppercase Roman numeralsformat=" i. "
– Lowercase Roman numeralsformat=" 000. "
– Numeral prefixformat=" 1- "
– Integer prefix/ hyphen prefix
为了指定不同级别的编号,例如源文档的节和子节,使用level
属性,它告诉处理器应该考虑源树的哪些级别。默认情况下,它设置为single
,如上面的示例所示。它还可以取multiple
和any
的值。count
属性是一个模式,告诉处理器要计算哪些节点(为了编号)。如果未指定,则默认为与当前节点相同的节点类型的模式。from
属性也可以用来指定计数应该从哪个节点开始。
当level设置为single
时,处理器会搜索与count
的值匹配的节点,如果不存在,它会匹配当前节点。当它找到匹配项时,它会创建一个节点列表并计算所有匹配类型的节点。如果列出了from
属性,它会告诉处理器从哪里开始计数,而不是计算所有节点。
当level为multiple
时,它不仅会计算一个节点类型的列表,它还会创建一个包含当前节点的所有祖先节点的列表,按照源文档中的实际顺序排列。创建此列表后,它会选择所有与count中表示的节点匹配的节点。然后它映射每个匹配count的节点的前导同级节点的数量。实际上,multiple
会分别记住所有节点。这就是any
与众不同的地方。它会按顺序编号所有元素,而不是在多个级别上进行计数。与其他两个值一样,可以使用from
属性告诉处理器从哪里开始计数,这实际上会将它分成多个级别。
这是上面示例使用level="multiple"
的修改版本。
图 18:排序和编号列表
<!--
Document: hotelNumbering2.xsl
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="tourGuide//hotelName"/>
</xsl:template>
<xsl:template match="hotel">
<xsl:number level="multiple"
count="city|hotel" format="
 1.1 "/>
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
图 19:结果 - hotelNumbering2.xsl
1.1 Bull Frog Inn 1.2 Pook's Hill Lodge 2.1 Pan Pacific Kuala Lumpur 2.2 Mandarin Oriental Kuala Lumpur
第一个模板匹配根节点,然后选择所有具有country
作为祖先的hotel
节点,创建一个节点列表。下一个模板递归地处理amenityName
元素,并根据属性中amenityName
实例的数量为每个实例提供一个编号。这是通过计算前面同级的数量再加上1来实现的。
格式化
[edit | edit source]格式化数字是一个简单的过程,因此本节将简要概述可以做些什么。放置在 XML 样式表中,可以使用函数在转换期间操作数据。为了使数字更容易阅读,我们需要能够将数字分成组,或者添加逗号或小数点。为此,我们使用format-number()
函数。此函数的目的是使用指定的模式将数值转换为字符串,这些模式控制前导零的数量、千位分隔符等等。此函数的基本语法如下:format-number (number, pattern)
数字
pattern
是一个字符串,它规定了数字的一般表示形式。字符串中的每个字符都代表数字中的一个数字或一些特殊标点符号,例如逗号或减号。
以下是用于在样式表中使用 format-number 函数时表示数字格式的字符及其含义
图 20:Format-number 函数
Symbol Meaning 0 A digit. # A digit, zero shows as absent. . (period) Placeholder for decimal separator. , Placeholder for grouping separator. ; Separate formats. - Default prefix for negative. % Multiply by 100 and show as a percentage. X Any other characters can be used in the prefix or suffix. ‘ Used to quote special characters in a prefix or suffix.
条件处理
[edit | edit source]有时需要根据条件显示输出。有两个指令元素可以让你有条件地确定哪个模板将根据某些测试来使用。它们是if
和choose
元素。
if
语句的测试条件必须包含在<xsl:if>
元素的test
属性中。测试大于和小于运算符的表达式必须分别用“>”和“<”来表示,以便进行适当的转换。XPath 中的not()
函数是一个布尔函数,如果其参数为假,则计算结果为真,反之亦然。and
和or
条件可以用来组合多个测试,但if
语句最多只能测试一个表达式。它也只能实例化一个模板的使用。
when
元素类似于 Java 中的else
语句。通过使用when
元素,choose
元素可以提供多种备选表达式。choose 元素必须包含至少一个 when 语句,但它可以包含任意多个 when 语句。choose 元素还可以包含一个 otherwise 元素实例,它就像 Java 程序中的最后一个 else 一样。如果其他表达式都不为真,它将包含该模板。
for-each
元素是另一个条件处理元素。我们在前面的章节练习中已经使用过它,因此这里将简要回顾一下。for-each
元素是一个指令元素,这意味着它必须是模板元素的子元素。for-each
根据select属性的值或表达式计算结果为一个节点集,并按文档顺序或排序顺序处理每个节点。
参数和变量
[edit | edit source]XSLT 提供了两个类似的元素,variable
和param
。两者都有一个必需的name
属性,以及一个可选的select
属性,并且你像这样声明它们
图 21:变量和参数声明
<xsl:variable name="var1" select="''"/> <xsl:param name="par1" select="''"/>
上面的声明已绑定到一个空字符串,这与你省略了 select 属性的效果相同。对于参数,此值仅被视为默认值或初始值,可通过命令行或使用with-param
元素的另一个模板进行更改。但是,对于变量,作为一个通用的规则,该值被设置并且不能动态更改,除非在特殊情况下。在进行声明时,请记住,变量可以在模板中的任何位置声明,但参数必须在模板的开头声明。
这两个元素也可以具有全局和局部作用域,这取决于它们定义的位置。如果它们在<stylesheet>
元素下最高级别定义,则它们具有全局作用域,可以在样式表中的任何位置使用。如果它们在模板中定义,则它们是局部的,只能在该模板中使用。在模板中声明的变量和参数仅对它们声明的模板以及它们下面的模板可见。它们具有级联效果:它们可以从顶层向下传递到一个模板中,再向下传递到该模板中的另一个模板中等等,但它们不能向上回溯!
我们将使用select
属性在参数的声明元素中硬编码一个值。
图 22:HTML 结果
<?xml version="1.0" encoding="UTF-8"?>
<!--
Document: countryParam.xsl
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:param name="country" select="'Belize'"/>
<xsl:param name="code" />
<xsl:template match="/">
<xsl:apply-templates select="country-codes" />
</xsl:template>
<xsl:template match="country-codes">
<xsl:apply-templates select="code" />
</xsl:template>
<xsl:template match="code">
<xsl:choose>
<xsl:when test="countryName[. = $country]">
The country code for
<xsl:value-of select="countryName"/> is
<xsl:value-of select="countryCode"/>.
</xsl:when>
<xsl:when test="countryCode[. = $code]">
The country for the code
<xsl:value-of select="countryCode"/> is
<xsl:value-of select="countryName"/>.
</xsl:when>
<xsl:otherwise>
Sorry. No matching country name or country code.
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
你传递的值不必用引号括起来,除非你传递一个包含多个单词的值。例如,我们可以传递 country="United States" 或 country=Belize 而不出现错误。
变量的值也可以用来设置属性值。以下是一个使用$code
变量中值的 countryCode 属性设置 countryName 元素的示例
图 23:countryCode 属性
<countryName countryCode="{$code}"></countryName>
这被称为属性值模板。请注意,参数周围使用了大括号。这告诉处理器将内容评估为表达式,然后将结果转换为结果树中的字符串。有些属性不能使用属性值模板设置
- 包含模式的属性(例如
apply-templates
中的select
) - 顶级元素的属性
- 引用命名对象的属性(例如
template
的name
属性)
参数虽然不是变量,但可以使用with-param
元素在模板之间传递。此元素有两个属性,name
是必需的,select
是可选的。以下示例使用 with-param 作为call-template
元素的子元素,尽管它也可以用作apply-templates
的子元素。
图 24:XSL With-Param
<?xml version="1.0" encoding="UTF-8"?>
<!--
Document: withParam.xsl
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates select="tourGuide/city"/>
</xsl:template>
<xsl:template match="city">
<xsl:call-template name="countHotels">
<xsl:with-param name="num" select="count(hotel)"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="countHotels">
<xsl:param name="num" select="''" />
<xsl:text>City Name: </xsl:text>
<xsl:value-of select="cityName" />
<xsl:text>
</xsl:text>
<xsl:text>Number of hotels: </xsl:text>
<xsl:value-of select="$num" />
<xsl:text>

</xsl:text>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="city">
在这里,我们匹配在apply-templates
节点集中返回的city
节点。call-template
,如前所述,调用名为countHotels
的模板。with-param
元素告诉被调用的模板使用名为num
的参数,并且 select 语句设置将被评估的表达式。- 请注意,参数的声明位于模板的第一行。它将
num
实例化为一个空字符串,因为该值将被with-param
元素的select
属性中表达式的值替换。 

在结果树中输出一个换行符,以使输出看起来更漂亮。
图 25:文本结果 - withParam.xsl
City Name: Belmopan Number of hotels: 2 City Name: Kuala Lumpur Number of hotels: 2
Muenchian 方法
[edit | edit source]Muenchian 方法是 Steve Muench 开发的一种使用键执行函数的方法。键通过将键值分配给节点并允许你通过键值访问该节点来工作。如果有很多具有相同键值的节点,那么当你使用该键值时,所有这些节点都会被检索。实际上,这意味着如果你想根据节点的特定属性对一组节点进行分组,那么你可以使用键将它们分组在一起。Muenchian 方法最常见的用途之一是将项目分组并计算该组中出现的次数,例如城市出现的次数。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:key name="Count" match="*/city" use="cityName" />
<xsl:template match="cities">
<xsl:for-each
select="//city[generate-id()=generate-id(key('Count', cityName)[1])]">
<br/><xsl:text>City Name:</xsl:text><xsl:value-of select="cityName"/><br/>
<xsl:text>Number of Occurences:</xsl:text>
<xsl:value-of select="count(key('Count', cityName))"/>
<br/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
文本结果 - muenchianMethod.xsl
City Name: Atlanta Number of Occurrences: 1 City Name: Athens Number of Occurrences: 1 City Name: Sydney Number of Occurrences: 1
数据类型
[edit | edit source]XSLT 中有五种不同的数据类型:节点集、字符串、数字、布尔值和结果树片段。变量和参数可以绑定到它们中的每一个,但最后一种类型是它们特有的。
节点集在 XSLT 中无处不在。我们已经看到它们从apply-templates
和for-each
元素以及变量中返回。现在我们将看到如何将变量绑定到节点集。检查以下代码
图 26:绑定到节点集的变量
<xsl:variable name="cityNode" select="city" />
...
<xsl:template match="/">
<xsl:apply-templates select="$cityNode/cityName" />
</xsl:template>
这里,我们将变量 `$cityNode` 的值设置为源树中节点集 `city`。`cityName` 元素是 `city` 的子元素,因此 `apply-templates` 生成的输出是 `cityName` 的文本节点。请记住,您可以在表达式中使用变量引用,但不能在模式中使用。这意味着我们不能使用引用 `$cityNode` 作为 `match` 属性的值。
如果您只对节点的文本感兴趣,而不是对整个节点集感兴趣,则字符串类型很有用。字符串类型使用 XPath 函数,最显着的是 `string()`。这只是一个简单的例子
图例 27:字符串类型
<xsl:variable name="cityName" select="string('Belmopan')" />
实际上,这是另一种更长的说法
图例 28:上述的简短版本
<xsl:variable name="cityName" select="' Belmopan'" />
也可以声明一个具有数字值的变量。您可以使用 XPath 函数 `number()` 来完成此操作。
图例 29:声明具有数字值的变量
<xsl:variable name="population" select="number(11100)" />
您可以使用 + - * / 等数值运算符对数字执行数学运算,以及一些内置的 XPath 函数,例如 `sum()` 和 `count()`。
布尔类型只有两个可能的值:真或假。例如,我们将使用一个布尔变量来测试是否已将参数传递到样式表中。
图例 30:测试布尔变量
<xsl:param name="isOk" select="''" />
<xsl:template match="city" />
<xsl:choose>
<xsl:when test="boolean($isOk)">
…logic here…
</xsl:when>
<xsl:otherwise>
Error: must use parameter isOk with any value to apply template
</xsl:otherwise>
</xsl:choose>
</xsl:template>
我们从参数 `isOk` 的空字符串声明开始。在 `when` 的 `test` 属性中,`boolean()` 函数测试 `isOk` 的值。如果该值为一个空字符串,就像我们默认定义的那样,`boolean()` 将评估为 `false()`,并且不会实例化模板。如果它确实具有一个值,并且它可以是任何值,`boolean()` 将评估为 `true()`。
最后的数据类型是结果树片段。本质上,它是一个可以包含标记的文本块(字符串)。在我们深入细节之前,让我们看一个例子
图例 31:结果树片段数据类型
<xsl:variable name="fragment">
<description>Belmopan is the capital of Belize</description>
</xsl:variable>
请注意,我们没有使用 `select` 属性来定义变量。我们不是选择一个节点并获取它的值,而是创建任意文本。相反,我们将其声明为元素的内容。变量标签的开始和结束之间的文本是结果树的实际片段。通常,如果您像之前那样使用 `select` 属性,并且在声明变量时没有指定内容,则元素为空元素。如果您不使用 `select` 并且您确实指定了内容,则内容是结果树。您可以像处理字符串一样对它进行操作,但与节点集不同,您不能使用 / 或 // 等运算符来访问节点。您从变量中检索内容并将其放入结果树的方法是使用 `copy-of` 元素。让我们看看如何做到这一点
图例 32:检索并放入结果树
<xsl:template match="city"
<xsl:copy-of select="cityName" />
<xsl:copy-of select="$fragment" />
</xsl:template>
结果树现在将包含两个元素:`city` 元素的副本和添加的元素 `description`。
EXSLT
[edit | edit source]EXSLT 是对 XSLT 的一组社区开发的扩展。模块包括处理日期和时间、数学和字符串的功能。
多个样式表
[edit | edit source]在前面的章节中,我们已经导入并使用了多个 XML 和模式文档。也可以使用 `import` 和 `include` 元素来使用多个样式表,这些元素应该很熟悉。还可以使用 XSLT 函数 `document()` 在一个样式表中一次处理多个 XML 文档。
包含外部样式表与我们在前面的章节中使用模式所做的非常相似。`include` 元素只有一个属性,即 `href`。它是必需的,并且始终包含指向文件位置的 URI(统一资源标识符)引用,该位置可以是本地的(在同一个本地目录中)或远程的。您可以根据需要包含任意多个样式表,只要它们位于顶层即可。如果您愿意,它们可以散布在整个样式表中,只要它们是 `<stylesheet>` 元素的子元素即可。当处理器遇到 `include` 的一个实例时,它会用包含文档中的所有元素替换该实例,包括模板规则和顶层元素,但不包括根 `<stylesheet>` 元素。所有项目只是成为样式表树本身的一部分,处理器对它们都进行相同的处理。以下是包含本地和远程样式表的声明
图例 33:本地和远程样式表的声明
<xsl:include href="city.xsl" />
<xsl:include href="http://www.somelocation.com/city.xsl"/>
由于 `include` 返回包含样式表中的所有元素,因此您需要确保您要包含的样式表不会包含您自己的样式表。例如,如果 `city_hotel.xsl` 有一个包含 `city.xsl` 的 `include` 元素,则 `city.xsl` 不能包含 `city_hotel.xsl`。当包含多个文件时,您需要确保您没有多次包含另一个样式表。如果 `city_hotel.xsl` 包含 `amenity.xsl`,`country.xsl` 包含 `amenity.xsl`,`city.xsl` 包含 `city_hotel.xsl` 和 `country.xsl`,则它间接包含了 `amenity.xsl` 两次。这会导致模板规则重复和错误。这些是一些令人困惑的规则,但如果您在包含之前仔细检查样式表,则很容易避免这些规则。
导入样式表和包含样式表之间的区别在于,导入的每个模板规则都有不同的导入优先级,而包含的样式表模板则合并到一个树中并正常处理。导入的模板形成一个导入树,包括根 `<stylesheet>` 元素,以便处理器可以跟踪它们的导入顺序。与 `include` 一样,`import` 也有一个属性 `href`,它是必需的,并且应该包含文档的 URI 引用。它也是一个顶层元素,可以根据需要使用多次。但是,它必须是 `<stylesheet>` 元素的直接子元素,否则会发生错误。此代码演示了导入本地样式表
图例 34:导入本地样式表
<xsl:import href="city.xsl" />
`import` 元素的顺序决定了匹配的模板将相互覆盖的优先级。最后导入的模板优先级高于最先导入的模板。但是,`template` 元素也有一个 `priority` 属性,它可以影响其优先级。`priority` 属性中的数字越大,优先级越高。导入优先级仅在模板冲突时生效,否则导入样式表与包含样式表并没有太大区别。处理冲突模板的另一种方法是使用 `apply-imports` 元素。如果导入文档中的模板与导入文档中的模板冲突,则 `apply-templates` 将覆盖该规则并导致调用导入的模板。
`document()` 函数允许您处理其他 XML 文档及其节点。该函数从使用表达式的任何属性(例如 `select` 属性)调用。例如
图例 35:`document()` 函数
<xsl:template match="hotel">
<xsl:element name="amenityList">
<xsl:copy-of select="document('amenity.xml')" />
</xsl:element>
</xsl:template>
当应用于仅包含一个空 `hotel` 元素的 xml 文档时,例如 `<hotel></hotel>`,结果树将添加一个名为 `amenityList` 的新元素,并将来自 `amenity.xml`(除 XML 声明外)的所有内容放置其中。`document` 函数可以接受许多其他参数,例如远程 URI 和节点集,仅举几例。有关使用 `document()` 的更多信息,请访问 http://www.w3.org/TR/xslt#document
XSL-FO
[edit | edit source]XSL-FO 代表可扩展样式表语言格式化对象,它是一种用于格式化 XML 数据的语言。在它创建时,XSL 最初被分成两部分,XSL 和 XSL-FO。这两部分现在正式称为 XSL。XSL-FO 文档定义了许多矩形区域以显示输出。XSL-FO 用于格式化 XML 数据以输出到屏幕、纸张或其他媒体,例如 PDF 格式。有关更多信息,请访问 https://w3schools.org.cn/xslfo/default.asp
总结
[edit | edit source]XML 样式表可以输出 XML、文本、HTML 或 XHTML。当 XSL 处理器转换 XML 文档时,它会将其转换为一个节点结果树,每个节点都可以根据样式表中包含的规则进行操作、提取、创建或保留。样式表的根元素是 `<stylesheet>` 元素。样式表包含顶层元素和指令元素。模板使用 XPath 位置来匹配源树中节点的模式,然后在找到匹配项时将定义的规则应用于节点。模板可以命名、具有模式或优先级。来自源树的节点集可以排序或格式化。XSLT 使用 `for-each` 和 `if` 元素进行条件处理。XSLT 还支持使用变量和参数。有五种基本数据类型:节点集、字符串、数字、布尔值和结果树片段。样式表还可以 `include` 或 `import` 其他样式表,甚至其他 XML 文档。XSL-FO 用于将数据格式化为矩形对象。 |
参考部分
[edit | edit source]图例 36:XSL 元素 (来自 https://w3schools.org.cn/xsl/xsl_w3celementref.asp 和 http://www.w3.org/TR/xslt#element-syntax-summary)
元素 | 描述 | 类别 |
apply-imports | 应用来自导入样式表的模板规则 | 指令 |
apply-templates | 将模板规则应用于当前元素或当前元素的子节点 | 指令 |
属性 | 添加一个属性 | 指令 |
attribute-set | 定义一个命名的属性集 | top-level-element |
call-template | 调用一个命名的模板 | 指令 |
choose | 与<when>和<otherwise>结合使用, 表达多个条件测试 |
指令 |
comment | 在结果树中创建注释节点 | 指令 |
copy | 创建当前节点的副本 (不包括子节点和属性) |
指令 |
copy-of | 创建当前节点的副本 (包括子节点和属性) |
指令 |
decimal-format | 使用format-number()函数定义将数字转换为字符串时使用的字符和符号 | top-level-element |
element | 在输出文档中创建元素节点 | 指令 |
fallback | 如果处理器不支持 XSLT 元素,则指定一个备用代码以运行 | 指令 |
for-each | 循环遍历指定节点集中的每个节点 | 指令 |
if | 包含一个模板,该模板仅在满足指定条件时才会应用 | 指令 |
import | 将一个样式表的内容导入另一个样式表。 注意:导入的样式表优先级低于导入的样式表 |
top-level-element |
include | 将一个样式表的内容包含到另一个样式表中。 注意:包含的样式表与包含的样式表具有相同的优先级 |
top-level-element |
key | 声明一个命名的键,该键可以在样式表中使用 key() 函数使用 | top-level-element |
message | 将消息写入输出(用于报告错误) | 指令 |
namespace-alias | 将样式表中的命名空间替换为输出中的不同命名空间 | top-level-element |
number | 确定当前节点的整数位置并格式化数字 | 指令 |
otherwise | 为<choose>元素指定默认操作 | 指令 |
output | 定义输出文档的格式 | top-level-element |
param | 声明一个本地或全局参数 | top-level-element |
preserve-space | 定义应保留空格的元素 | top-level-element |
processing-instruction | 将处理指令写入输出 | 指令 |
sort | 对输出进行排序 | 指令 |
strip-space | 定义应删除空格的元素 | top-level-element |
stylesheet | 定义样式表的根元素 | top-level-element |
template | 匹配指定节点时应用的规则 | top-level-element |
文本 | 将文字文本写入输出 | 指令 |
transform | 定义样式表的根元素 | top-level-element |
value-of | 提取选定节点的值 | 指令 |
variable | 声明一个本地或全局变量 | 顶级元素或指令 |
when | 为<choose>元素指定操作 | 指令 |
with-param | 定义要传递到模板的参数值 | 指令 |
图 37: XSLT 函数 (来自 https://w3schools.org.cn/xsl/xsl_functions.asp)
名称 | 描述 |
current() | 返回当前节点 |
document() | 用于访问外部 XML 文档中的节点 |
element-available() | 测试 XSLT 处理器是否支持指定的元素 |
format-number() | 将数字转换为字符串 |
function-available() | 测试 XSLT 处理器是否支持指定的元素 |
generate-id() | 返回一个唯一标识指定节点的字符串值 |
key() | 使用<xsl:key>元素指定的索引返回节点集 |
system-property | 返回系统属性的值 |
unparsed-entity-uri() | 返回未解析实体的 URI |
图 38: 继承的 XPath 函数 (来自 https://w3schools.org.cn/xsl/xsl_functions.asp)
节点集函数
名称 | 描述 | 语法 |
count() | 返回节点集中的节点数量 | number=count(node-set) |
id() | 根据其唯一 ID 选择元素 | node-set=id(value) |
last() | 返回处理的节点列表中最后一个节点的位置编号 | number=last() |
local-name() | 返回节点的本地部分。节点通常由前缀、冒号和本地名称组成 | string=local-name(node) |
name() | 返回节点的名称 | string=name(node) |
namespace-uri() | 返回指定节点的命名空间 URI | uri=namespace-uri(node) |
position() | 返回当前正在处理的节点在节点列表中的位置 | number=position() |
字符串函数
名称 | 描述 | 语法和示例 |
Concat() | 返回所有参数的串联 | string=concat(val1, val2, ..) 示例 |
contains() | 如果第二个字符串包含在第一个字符串中,则返回 true 字符串,否则返回 false |
bool=contains(val,substr) 示例: |
normalize-space() | 删除字符串开头和结尾的空格 | string=normalize-space(string) 示例: |
starts-with() | 如果第一个字符串以第二个字符串开头,则返回 true, 否则返回 false |
bool=starts-with(string,substr) 示例 |
string() | 将值参数转换为字符串 | string(value) 示例 |
string-length() | 返回字符串中的字符数 | number=string-length(string) 示例 |
substring() | 返回字符串参数中字符串的一部分 | string=substring(string,start,length) 示例 |
substring-after() | 返回字符串参数中出现在 substr 参数中的子字符串之后的字符串部分 | string=substring-after(string,substr) 示例 |
substring-before() | 返回字符串参数中出现在 substr 参数中的子字符串之前的字符串部分 字符串 |
string=substring-before(string,substr) 示例 |
translate() | 获取值参数,将所有出现的 string1 替换为 string2,并返回修改后的字符串 |
string=translate(value,string1,string2) 示例 |
数字函数
名称 | 描述 | 语法和示例 |
ceiling() | 返回不小于数字参数的最小整数 | number=ceiling(number) 示例 |
floor() | 返回不大于数字的最小整数 参数 |
number=floor(number) 示例 |
number() | 将值参数转换为数字 | number=number(value) 示例 |
round() | 将数字参数舍入到最接近的整数 | integer=round(number) 示例 |
sum() | 返回节点集中一组数字值的总值 | number=sum(nodeset) 示例 |
布尔函数
名称 | 描述 | 语法和示例 |
boolean() | 将值参数转换为布尔值,并返回 true 或 false | bool=boolean(value) |
false() | 返回 false | false() 示例 |
lang() | 如果语言参数与 xsl:lang 元素的语言匹配,则返回 true,否则返回 false | bool=lang(language) |
not() | 如果条件参数为 false,则返回 true,如果条件参数为 true,则返回 false | bool=not(condition) 示例 |
true() | 返回 true | true() 示例 |
练习
[edit | edit source]为了更多地了解 XSL 和样式表,练习 被提供。
答案
[edit | edit source]为了更多地了解 XSL 和样式表,答案 被提供。