为了深入理解XSLT 编程,你必须首先理解 XML,因为 XSLT不只是负责转换 XML 而且自身还是一种完全意义上的XML标准语言。在理论上,你完全可以编写负责自身格式转换的XSLT风格表单,当然这是一件满有意思的事,只是没啥用处。
让我们回忆一下, XML 并不是一种通常意义上的语言,XML是一种元语言(metalanguage),也就是建立XML规范语言的结构(比如XSL和 XHTML就是XML规范语言)。HTML看起来很象XML,但实际上违反了好些 XML规则。
XML 语言定义了一套用来把数据标记为元素(或者可以说节点)的标签。比方说,就XHTML语法而言,<table>标签就等于开始标记某个特定的XML节点。XML节点可以包含属性和内容体。属性是由字符串组成的名字/值对。内容体可以是字符串和/或更多的
XML节点。这就意味着,XML是一种层次化的结构,可以表示很复杂的数据格式。我们不妨考虑以下的一个XHTML片段:
<table>
<tr>
<td>Hello world!</td>
<td><img src="smiley.gif"/></td>
</tr>
</table>
在以上的代码段中,每个节点都有自己的开-闭标签,两个标签之间是更多的节点和文本字符串。img 节点有一个src属性而没有内容,紧挨着开标签的是一个终止斜线。这个终止斜线和文本都在<td>节点内嵌套,而后者又在<tr>节点内嵌套,显然<tr>节点则在<table>内嵌套。
XSLT 的核心思想是建立上下文环境(context),也就是在XML文档内的特定节点或者整套节点同时输出为存在于这个环境内的格式化数据版本。为此, XSLT风格表单被分解为离散的模版,每个模版负责处理XML文档内某类型的标签。在这些模版内,XSLT要用到标量、传递参数、循环条件以及其他转换XML的元件。
<xsl:stylesheet>元素是任何XSLT风格表单的最外层元素,你要为其指定版本和一个或者多个名称空间(namespace):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform>
...
</xsl:stylesheet>
你可以设置其他属性,但是,对几乎所有的基本风格表单来说,你可以原样使用这些<xsl:stylesheet>标签。其中就可以嵌套模版元素了。
<xsl:template>元素定义了随导出结果输出而伴随的指令上下文环境,其语法如下所示:
<xsl:template
match="expression"
name="name"
priority="number"
mode="mode">
</xsl:template>
XSLT 处理器在发现风格表单中的一个显式调用或者在源XML文档中发现匹配节点之后就会执行<xsl:template>。最常见的情况是当XSLT处理器扫描XML时遇到了匹配节点。匹配属性则用Xpath表达式标识模版中取出的节点。
激活的 <xsl:template> 元素输出其需要的内容。这些内容可能由文本和非XSLT的标记所组成并直接写入某个新建文档乃至更多的XSLT元素,后者则在匹配节点的上下文环境中执行。跟踪上下文环境是不可能的。XSLT元素只处理被模版激活的同类节点。
多个模版可以匹配一个节点。在这种情况下,采用模式和优先级属性的复杂规则确定了应由哪个模版来处理节点。最简单的风格表单只包含了匹配给定节点的一个模版。
对那些主要包含标记文本(比如HTML)的XML文档,你的XSLT风格表单很可能会包含你能遭遇的每个标签的一个模版。而对那些包含高度结构化层次数据的XML文档,你的风格表单可能只会包含顶级节点的模版。这些模版知道数据的结构并会直接访问子节点而不是跳到其他模版。
比方说,示例XML文件包含了一个较短的标记图书数据。它由一个<book>节点组成,而该节点则包含了<title>和多个<chapter>节点。这一模版会在顶级的<book>节点内执行每个<chapter>节点:
<xsl:template match="/book/chapter">
This is chapter <xsl:number/>, entitled "<xsl:value-of select="title"/>"
</xsl:template>
假如某个 XSLT处理器没有针对节点或其父节点的匹配模版,它就会输出该节点的内容,不过这样做可能包含偏离其自身模版的子节点。 所以只处理模版的风格表单会产生以下的结果:
<?xml version="1.0" encoding="utf-8"?>
Stuff Happens
This is chapter 1, entitled "How it begins"
This is chapter 2, entitled "What transpires"
This is chapter 3, entitled "Where it ends"
显然,<paragraph>节点被忽略了,因为它们的<chapter> 父节点被处理了,但是第1个<title>并不在<chapter>之内所以干脆打印了事。