IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  XML  >

准备从 XSLT 1.0 升级到 2.0,第 1 部分: XSLT 的改进

XSLT 2.0 的主要特性及其所解决的 1.0 不足

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 中级

David Marston (David_Marston@us.ibm.com), 软件工程师, IBM
Joanne Tong (joannet@ca.ibm.com), 软件开发人员, IBM

2006 年 11 月 07 日

XSLT 2.0 引入了许多新特性,部分是为了克服 XSLT 1.0 的不足而专门设计的。本文探讨了其中一些最值得期待的特性:分组、隐含文档节点(Implicit Document Nodes)、用户定义函数、日期时间处理、模式感知以及输出方面的诸多改进。本文中的例子包括用纯 1.0 语法编写的常见应用程序,以及用更简单、更强大的 2.0 语法改写的版本。

关于本系列文章

W3C 发布的最新规范 XSLT 2.0 是一种转换 XML 文档的语言。它包括很多新的特性,部分是为克服 XSLT 1.0 的不足而设计的。在本系列文章中,我们将从希望解决老问题、学习新技术并愿意了解可以从 XSLT 2.0 中期待什么的 XSLT 1.0 用户的角度出发,从高层概括和深入剖析两方面考察 XSLT 2.0。为了便于升级,本文提供了源自常见应用程序的例子和实践方面的建议。为了帮助您开始使用 XSLT 2.0,本文还提供了相应的迁移技术。





回页首


XSLT 变得越来越大越来越好

XSLT 被广泛采纳和应用于各种问题,范围之广超出了 XSL 工作组(WG)的预料。如果编写过 XSLT 样式表,您可能知道通过某些编码模式来获得超出代码表面含义之外的结果。看到 generate-id() 的时候您的脑子里会立刻想到什么呢?是 node-set() 呢? 还是 substring-before() 呢?或是 disable-output-escaping 呢?XSLT 2.0 很快就将成为最终的推荐标准,可以用它使您的代码更容易阅读。新的 XSLT 特性允许您使用更接近所要表达的含义的术语。

XSLT 1.0 主要在两个 W3C 文档中定义:XSLT 和 XPath。2.0 版是为了适应 XQuery 的步伐而设计的,现在 XSLT 家族的核心包括六个规范:XSLT、XPath、Functions and Operators (F&O)、Data Model (XDM)、Formal Semantics 和 Serialization(W3C 文档的链接请参见 参考资料)。与 1.0 类似,它也建立在其他几种基础规范之上(XML、名称空间等),并且把 XML Schema 纳入了自己的轨道。此前的需求文档申明了要实现的新特性的目标,肯定了开发 2.0 规范的必要性。

本文以 2006 年 6 月的候选推荐标准为基础,描述了 XSLT 2.0 中那些最有可能说服您升级到这个新版本的特性。虽然从候选推荐标准到正式推荐标准 W3C 可能会做一些细节上的修改,但是本文的大部分内容仍然适用。





回页首


分组

XSLT 1.0 中出现最早的专门技术是处理一组元素的方式,其中每个元素都有一个属性(或后代)表明它所属的组。1.0 规范中没有解决元素分组。早期的 1.0 样式表作者提炼了一种技术,虽然在代码中不容易阅读,但是通常可以通过 for-each 循环来识别,其形式为 <xsl:if test="generate-id(.)=generate-id(set[@grp=$thisvalue][1])">... once-per-group code ...</xsl:if>,其中比较 generate-id() 的值是为了确定当前节点是否是 $thisvalue@grp 属性中的第一个节点。如果需要筛选很大的节点集(population),这种每次使用不同的值(该例中为 $thisvalue)作为键的 is-it-the-first-node 方法效率可能很低。

2.0 中分组是通过新的 xsl:for-each-group 指令和两个支持函数实现的,即 current-group()current-grouping-key()。它们作用于一组类似的节点,与 xsl:for-each 以及 current() 函数非常相似。并且和 for-each一样,for-each-group 允许使用 xsl:sort 作为子元素来控制分组处理的顺序。如果熟悉 xsl:for-each,就会发现 xsl:for-each-group 用起来简单而且自然。分组应该能够改进性能,因为处理程序能够优化检测分组边界的方式。关于分组和获得目标结果的技术有很多内容可以写,因此本文仅仅介绍几种比较简单的应用,比如对元素总体分组,这是大多数场合中的情形。

对总体分组有四种方法。其中三种方法考虑到了分组节点出现的顺序,第四种采用 SQL 用户熟悉的方式根据键值分组。每次使用 xsl:for-each-group 指令时,都要指定其中的一种方法:

group-by 方法中,需要定义一个表达式说明从何处取得总体中每个成员的键值(类似于 xsl:key 中的 @use,或 xsl:sort 中的 @select)。通常情况下,包含多个元素的总体根据已知的每个元素具有的属性进行分组。假设您有组织中每个分支机构的财务数据,每个分支机构都被指定了一个地理区域。在 XML 中,数据可能采用下面这种形式:


清单 1. 需要分组的数据
<branch id="B723" region="Northeast">...data...</branch>
<branch id="B201" region="North">...data...</branch>
<branch id="B558" region="Northeast">...data...</branch>
<branch id="B064" region="West">...data...</branch>
etc.

为了将 <branch> 元素收集到区域中,可使用下面的指令: <xsl:for-each-group select="branch" group-by="@region">... once-per-region code...</xsl:for-each-group>。在循环体中,current-grouping-key() 保存了当前的 @region 值,可以使用 <xsl:for-each select="current-group()">...once-per-branch code...</xsl:for-each> 迭代当前的分组,即同一个区域中的所有分支机构。要注意,不需要建立分组就能很容易得到所有 grouping-key 值(该例中为区域名称)的列表,只要使用新的 distinct-values() 函数即可。关于 distinct-values 函数的更多信息,请阅读文章“XML for Data: What's new in XPath 2.0?”(参阅 参考资料)。

group-by 方法不关心节点的顺序。它允许多个键,提高了一个分组中重复节点的可能性。规则是在任何给定的分组中,每个成员节点只能出现一次。但是,同一个节点可以是多个组的成员。

设计 group-ending-with 方法是考虑到按照一定顺序排列、并且带有标志 节点(通常表示一个分组结束)的元素总体。比方说在数字应用程序中,经常在列表中遇到 <subtotal><total><grand-total> 这样的标志。在 xsl:for-each-group 中,@group-ending-with 使用 XSLT 范式语法指定如何识别标志节点。用 XSLT 1.0 实现相同的功能可能非常麻烦,需要结合使用 position()、递归处理、preceding-sibling 轴,而且可能需要分为两个阶段来处理。

group-starting-with 方法与 group-ending-with 类似,但标志节点出现在分组的开始。在处理莎士比亚戏剧的 XML 版本时可使用这种形式的分组(这种版本已经在 Web 上存在很多年了)。<speech><line> 元素按照顺序出现。说话的角色用行前面单独的元素表示,一个场景中在需要的时候会出现 <StageDir> 元素,但遵循一定的顺序。在某些应用中,可能希望某些兄弟元素是嵌套的或者相反,但是分组可以克服这些问题。一种可能是标志 <StageDir> 元素在某一剧情有效的时候对所有其他元素分组。

第四种分组方法是 group-adjacent。与 group-by 类似,它也使用节点值而不是范式来划分组,不过它能够识别总体中元素的顺序。因此,它用于监视每个元素都有的某个节点(属性或后代),当该节点的值不同于上一个值时,则划分出一个组。它可用于划分固定大小的分组,使用下面这种很快就会盛行的技术:

<xsl:for-each-group select="item" group-adjacent="ceiling(position() div 4)">

它把 <item> 元素每四个分成一组。

顺便说一下,XSLT 2.0 提供了一些运算符可以更方便地检查节点顺序或者节点相等。这一节开始使用的 generate-id() 实际上是判断两个路径是否指向同一个节点。在 2.0 中为此提供了一个运算符 is。类似地,>> 和 << 运算符用于检查节点的文档顺序在另一个之后还是之前。这些运算符可以简化 xsl:for-each-group 不能解决的任何可以预见的需要。

关于分组还有很多其他技巧和技术。这一节只是让您对其可能性有一个总体了解。





回页首


隐含文档节点

XSLT 1.0 受到挫折的主要原因之一是结果树片段(Result Tree Fragment,RTF)的局限性。虽然表示树的变量可以复制到结果集或者将其看作一个字符串,但是不能使用标准 XSLT/XPath 工具像树那样导航。也许和其他很多人一样,您曾经反复琢磨 11.1 节中的这样一段话,“结果树片段被视作相当于只包含一个根节点的节点集。但是结果树片段支持的操作是节点集所允许的操作的一个子集。仅当一个操作能用于字符串时才允许用于结果树片段……” 读到这里,您可能奇怪为什么它不能作为树用 XPath 表达式导航。

多数 XSLT 处理程序的提供商都增加了一个扩展函数,通常称为 node-set(),它可以把 RTF 变成可导航、可筛选的树。还记得在 1.0 中,xsl:variable 可以有属性 @select,它的值可以是一个节点集或者单个原子节点,或者以内容构造器作为子元素来创建 RTF。后一种形式建立了 2.0 中的隐含文档节点(Implicit Document Node,IDN),这是一种新增加的临时树 结构。如果 1.0 样式表创建了 RTF 变量,2.0 版相同的样式表将创建 IDN。如果需要 xsl:choosexsl:call-template 这样的编程指令,xsl:variable 中仍然需要内容构造器(2.0 中重新命名为序列构造器),从而迫使您创建 RTF/IDN 树。与以前一样,可以使用 xsl:copy-of 将整个 IDN 树复制到输出,但是也可在路径表达式中使用该变量而不需要转换成节点集。

IDN 或其子树可以出现在 xsl:apply-templates 的 select 表达式中,允许按照与 XML 输入文档相同的方式处理。如果很大,这是一种两阶段转换的形式,但是不需要将处理程序的运行划分成阶段 1 和阶段 2。如果需要构造带有大量交叉引用的 XML 文档,则划分为两个或更多阶段比较方便。不用节点集函数避免了出现树的两个副本的弊端,一个 RTF 和一个节点集容易造成混乱。对于很大的临时树,这还有助于节约内存。如果构造 IDN 时建立了 ID 类型的属性或者适当的键值,可以使用 id()key() 函数进行索引访问,这些函数的 2.0 版还增加了一个方便的参数,以限制检索到目标树中的索引。

IDN 变量引用可以后跟路径步(如 /child)和筛选器(如 [@foo="yes"]),从而为 xsl:for-each 循环选择遍历的节点集,或者在其他 select 表达式中使用的地址值。同样也能应用于 xsl:ifxsl:when 中的 test 表达式。

注意,xsl:variable 提供了控制数据类型化的选项。因而,xsl:variable 能够与序列构造器内容结合在一起代替 @select,不用创建完整的 IDN,而是创建独立的属性节点、注释和处理指令。它可以创建独立的包含内容的元素,与 IDN 不同的是,这些元素在树的顶端没有文档节点。它也可以创建原子值序列,这有可能与结点集混淆。下面的例子说明了如何使用 @as 避免创建文档节点和任何元素,而是创建一个序列。


清单 2. 用 @as 修饰的变量作为 xs:double 值的序列,没有文档或元素节点

<xsl:variable name="values" as="xs:double*">
 <xsl:sequence select="(1,3,5)"/>
 <xsl:if test="@m='large'">
 <xsl:sequence select="(2,4,10)"/>
 </xsl:if>
</xsl:variable>

变量 $values 既不是节点集也不是 IDN,仅仅是一个原子性的双精度数序列。(注意,本文中的名称空间前缀 “xs” 指向 XML Schema 数据类型名称空间。)





回页首


用户定义函数

XSLT 2.0 引入的新指令 xsl:function 允许样式表作者使用纯 XSLT 语言和语义在样式表中创建自己的函数。当然,只有在 F&O 和 XSLT 2.0 规范(参阅 参考资料)所提供的 128 个函数和 68 个运算符中没有找到需要的函数时才需要这样做。xsl:function 的例子请参阅后面 字符映射 一节。

由于不能方便地创建用户定义函数,XSLT 1.0 样式表作者常常采用其他方法来达到目的,比如调用命名模板、用其他语言定义自己的扩展函数、或者希望 XSLT 1.0 处理程序支持伪标准的扩展库,如 EXSLT(EXSLT 站点链接请参阅 参考资料)。F&O 库中的一些函数和 EXSLT 中定义的函数重叠,但是两者都提供了对方所不具备的函数。EXSLT 也定义了扩展指令 func:function,语法与 xsl:function 相同(如果使用过 func:function,采用 XSLT 2.0 的时候应该改过来)。使用 EXSLT 扩展的不利之处在于有些实现仅提供了有选择地支持,而且不能保证将来继续支持这些扩展。

用其他语言编写自己的扩展函数通常费时费力。您的扩展函数实现可能仅限于某一个处理程序,而且总是受到那个处理程序所公开的机制和数据结构的限制。在 XSLT 1.0 中,编写命名模板来获得需要的功能是最安全的方法,没有依赖于特定实现的局限性。但是调用命名模板需要更多的 XSLT 指令,调用函数则更紧凑而且很容易嵌入到 XPath 表达式中。另一方面的局限与从 xsl:variable 内容中调用命名模板时的结果树片段有关(参阅上一节 隐含文档节点)。如果由于需要某个扩展函数实现,1.0 样式表不能在 2.0 处理程序上运行,首先应检查 F&O 和 XSLT 2.0 规范,看看有没有合适的函数。如果没有找到需要的函数,可以尝试在样式表中编写自己的函数。对部分但不是全部 EXSLT 函数,EXSLT 提供了基本等价的命名模版,可以此为指导用 XSLT 语法编写自己的函数(要记住 EXSLT 是为 XSLT 1.0 提供的,因此使用新的 2.0 语法可以进一步简化函数)。如果发现扩展函数比样式表函数更好,可以定义样式表函数作为扩展函数不能使用时的第二选择。





回页首


日期/时间数据

XSLT 2.0 标准家族提供了对日期/时间数据的支持,包括一组函数和运算符,可执行与时间有关的计算。不再需要将日期和时间作为字符串来处理,提取部分字符串作为单个数字或者月份的名称。该数据类型来自 XML Schema Part 2 规范(参阅 参考资料),它提供了时间点和时期类型。如果启用了模式感知(参阅后面的 模式感知),这些类型的值可以来自转换的输入文档或者函数的返回值。一些 XSLT 2.0 指令提供了断言数据类型的机制,比如新增的 @as 属性(参阅 “隐含文档节点” 中的 @as 例子)。

如果需要创建与时间相关的数据类型的单个值,可以使用类型构造器函数并提供时间的字符串表示形式作为参数。比如,xs:time('12:34:56') 接受时间的字符串表示形式,并返回一个类型注释被标记为一天中某个时刻的值。这类构造器函数的名称与 XML Schema 的内置类型名相同:xs:date、xs:time、xs:dateTime、xs:duration、xs:gYear、xs:gMonth、xs:gDay、xs:gYearMonth、xs:gMonthDay,并增加了 xs:yearMonthDurationxs:dayTimeDuration

得到表示为一天中某个时刻的值 12:34:56 后,可以加上一个时期值来得到稍后的一个时刻。提供了很多新的函数和运算符在时间点和时期之间完成适当的算术运算。可以用时期值乘上一个标量值得到更长或更短的时期值,也可以除一个标量值或者另一个时期。两个 dateTime 相减的差是一个时期值。类型相同的两个值可以比较。比如,PT90S(90秒)的 dayTimeDuration 与规范化值 PT1M30S(1 分钟 30 秒)进行比较是相等的。有些类型还定义了不等于关系。针对时期的大于表示更长的时期;针对日期的大于表示较晚的日期。

此外,还有一组名称浅显易懂的分量抽取 函数,只要一个操作就能从日期、时间或时期数据中抽取一个字段。比如,

minutes-from-dateTime(xs:dateTime('2005-07-01T14:06:32'))
 

返回整数 6,而在过去要取出分钟数,需要混合使用 substring-before()substring-after() 函数,还可能要用到 number()。在新方法中,函数名清楚地表明了要做什么。

在 1.0 样式表中如果要大量处理时间数据,可能需要执行一些调用专门命名模板的操作。如果模板的返回值需要提供给 XPath 表达式时,这样做很不便,因为需要用 xsl:variable 将模板调用包围起来,该变量是一个 RTF(参阅 隐含文档节点)。一般来说,xsl:function(参阅 用户定义函数)是 2.0 中采用的方法,但是对于时间相关数据,有 24 个新函数和 44 个新运算符再加上构造器函数可供选择。相对而言,一些 XSLT 1.0 处理程序提供的 EXSLT 包(参阅 参考资料)只有 28 个与时间有关的函数,包括一个格式化函数。

根据用户广泛的要求,新函数集也提供了返回当前时间、日期或者 dateTime 的函数,因此样式表可以在输出中打上时间戳。

上述都是按照对应的实际类型处理的情况。这些函数是 XSLT 和 XQuery 共用的。因为 XSLT 还承担着生成可表示信息的使命,XSLT 增加了接受 date、time 或 dateTime 类型值并生成带有所需修饰的字符串的函数,比如月份名称的拼写。XSLT 2.0 规范允许实现选择不同的语言、数字等类似的方面。如果这些方面很重要,现在就可以考虑组成需要的列表,在规范完成之后检查 2.0 处理程序是否有自己需要的特性。





回页首


模式感知

XSLT 2.0 中模式感知是一种可选特性。如果处理程序支持它,可以发现这是一种有用的特性。模式感知特性主要用于错误检查。它可以用 XML 模式验证输入和输出,允许根据 Schema 类型引用样式表中的源节点。对于 1.0 样式表作者而言,该特性可以在调试受挫之前预先发现错误。可通过下列机制利用这一特性:


表 1. 支持模式感知特性的指令属性和 XPath 成分
语法项用法
@as 属性xsl:variable、xsl:param、xsl:functionxsl:template 上使用该属性来断言生成的结果树或序列是目标 Schema 类型的实例。
instance of 对序列、单个原子值或节点使用该运算符来检验是否为目标 Schema 类型的实例。
@validation@type 属性[对于 Literal Result Element (LRE) 则为 @xsl:validation@xsl:type]使用这些属性根据 Schema 类型定义或者顶层元素和属性定义来验证节点。
cast as 和构造器函数将原子类型强制转换成 Schema 类型。
castable as 检查能否进行强制类型转换的运算符。
node-test element(*,type)attribute(*,type) 仅返回标记为目标 Schema 类型的元素或属性节点。
node-test schema-element(elemDecl)schema-attribute(attribDecl) 返回与 Schema 元素或属性定义匹配的元素或属性节点。





回页首


输出(序列化)的改进

xsl:result-document

一个 2.0 样式表可以生成多棵结果树,在不同的位置进行序列化。developerWorks 文章 “Create multiple files in XSLT 2.0”(参阅 参考资料)就如何使用新的 xsl:result-document 指令做了一般性介绍。

字符映射

1.0 样式表作者常常错误地使用 disable-output-escaping(d-o-e)属性,没有充分理解标记和文本之间的区别。这也是体系结构上的一个缺陷,因为它要求序列化程序除了知道 XPath 数据模型和 Serialization 参数公开的信息之外,还要知道每个节点的附加字段。这样就模糊了转换和序列化之间的界限,因此 XSL WG 不赞成使用该属性。他们在 2.0 中引入了字符映射,这是在序列化过程中进行的字符映射,出现的特定字符都要被映射成 use-character-maps Serialization 参数所要求的另一个字符,即便造成 XML 不再是格式良好的。下面的样式表说明了如何使用字符映射,用 my:d-o-e 函数替换 @disable-output-escaping。也可使用该函数作为一个例子,仅对结果树中选定节点使用字符映射。这个例子依赖于映射不常用的字符,如左尖引号(«,通常编码为 «),并假设这样的字符不出现在结果序列中。如果需要保证只有目标字符被映射,可以使用更少见的字符。


清单 3. 在输出中得到特殊 XML 字符的更好办法
 
<xsl:output use-character-maps="my:charMap" />

<xsl:character-map name="my:charMap">
 <xsl:output-character character="«" string="<" />
 <xsl:output-character character="»" string=">" />
 <xsl:output-character character="§" string="&" />
 <xsl:output-character character="¤" string="'" />
 <xsl:output-character character="¦" string=""" />
</xsl:character-map>
 
<xsl:function name="my:d-o-e" as="xs:string">
 <xsl:param name="str" as="xs:string" />
 <xsl:sequence select="translate($str, '<>&'"',
 '«»§¤¦')" />
</xsl:function>
 
<xsl:template match="/doc">
 Replace
 <xsl:value-of select="." />
 With
 <xsl:value-of select="my:d-o-e (.)"/>
</xsl:template>

使用下列字符映射避免转义所有的与符号(&)、小于号(<)、大于号(>)、单引号(')和双引号(")字符。虽然似乎仅仅是把每个字符映射为自身,它实际上是避免了 Serialization 中的转义步骤(根据 XML 或 HTML 规则),因为该步骤不能应用于被映射的字符本身。


清单 4. 保留特殊 XML 字符为非转义形式的另一种方法
 
    <xsl:character-map name="my:charMap">
 <xsl:output-character character="<" string="<" />
 <xsl:output-character character=">" string=">" />
 <xsl:output-character character="&" string="&" />
 <xsl:output-character character="'" string="'" />
 <xsl:output-character character=""" string=""" />
</xsl:character-map>

Unicode 规范化

Unicode 规范化是 XSLT 用户的要求,结果 XSLT 2.0 提供了两种执行规范化的选择。要实现选择性规范化,可使用 F&O 函数 normalize-unicode()。另外,Serialization 参数 normalization-form 将影响到整个最终结果树的规范化。如果使用字符映射,则首先考虑是否对映射字符进行规范化,还是对映射后的字符规范化。第一种情况应使用 normalize-unicode(),后者则使用参数 normalization-form

URI 转义的三个函数

F&O 规范定义了三个函数:encode-for-uri()、iri-to-uri()escape-html-uri(),来执行构成 URI 或其一部分的字符串的百分号编码。在 URI 中,可以通过用百分号后面将两位十六进制数字替换实际字符来转义具有语法意义的字符,比如 %20 表示空格。EXSLT 定义的 str:encode-uri() 也执行百分号编码,但是与 encode-for-uri() 相比少了 5 个保留字符。根据需要,F&O 中的这三个函数应该足够了。为了控制 HTML 输出中转义 URI 属性,可使用 escape-uri-attributes Serialization 参数。该参数不能用于 XSLT 1.0,因此 URI 转义是不可避免的。

下面是 US-ASCII 编码字符集中这些函数将采用百分号编码的的可打印字符列表,码值为 32 到 126(十进制):

  • fn:iri-to-uri() 编码 " < > \ ^ ` { | } space
  • EXSLT 的 str:encode-uri() 编码 " # $ % & + , / : ; < = > ? @ [ \ ] ^ ` { | } space
  • fn:encode-for-uri() 编码 ! " # $ % & ' ( ) * + , / : ; < = > ? @ [ \ ] ^ ` { | } space
  • fn:escape-html-uri() 不对这些字符编码

创建名称空间节点

新增的 xsl:namespace 指令显然是用于创建名称空间节点的简单方法,解决了 XSLT 1.0 缺少这一机制的问题。在 XSLT 1.0 中,天真的用户可能曾经尝试用 xsl:attribute 并以 “xmlns:prefix” 作为其名称来创建名称空间。名称空间节点实际上只能间接创建,要么从源文档中复制元素或者名称空间节点,如果实例化了 LRE,则可以从样式表中复制名称空间,要么直接创建其名称在名称空间中的元素或属性。换句话说,在 1.0 中,您要么使用已经创建的名称空间,要么创建其他不需要的节点作为副本来生成名称空间节点。

XML 1.1(前缀未声明和 unicode 规范化)

可以用符合 XSLT 1.0 的处理程序作为最后修正版本来支持 XML 1.1(参阅 参考资料)。XML 1.1 对于 XSLT 1.0 和 2.0 都是可选的。但是如果支持,仍然要明确输出属性 normalization-formundeclare-prefixes 只能用于 XSLT 2.0,而不能用于 XSLT 1.0。

XHTML 输出方法

XSLT 2.0 增加了一种新的输出方法 XHTML,所有符合 Serialization 规范的处理程序都必须支持它。在 XSLT 1.0 中也能序列化为 XHTML,只要输出为 XML 并设置 xsl:outputdoctype-systemdoctype-public 属性即可。这样可以保证 <!DOCTYPE> 声明被序列化。但是,由于 XHTML 规范 Appendix C 中列出的细微差别(参阅 参考资料),对于某些设计用于处理 HTML 的代理来说,这样可能还不够。

xsl:value-of 的分隔符属性

可能最受青睐的 XSLT 1.0 指令是 xsl:value-of,用于把值作为字符串放到输出中。很多样式表作者使用它作为调试工具,但是又发现如果不清楚 expr 有多少节点,还是必须使用 count(expr)。在 2.0 中,xsl:value-of 不仅仅输出集合中的第一个成员,实际上遍历整个序列,甚至可以控制 separator 字符串(如果需要 xsl:value-of 仅返回第一个成员,只要 2.0 处理程序支持向后兼容,可以在指令中设置 version="1.0")。<xsl:value-of select="/doc/*" separator="', '"/> 这行简单的指令将替换下面所有的代码:


清单 5. 格式化值列表的典型 1.0 代码,2.0 中只要一条指令就能完成

  <xsl:template match="/doc">
 <xsl:call-template name="valueOfWithSeparator">
 <xsl:with-param name="nodes" select="*" />
 <xsl:with-param name="separator" select="', '" />
 </xsl:call-template>
 </xsl:template>

 <xsl:template name="valueOfWithSeparator">
 <xsl:param name="nodes" />
 <xsl:param name="separator" select="' '" />
 <xsl:for-each select="$nodes">
 <xsl:if test="not(position() = 1)">
 <xsl:value-of select="$separator" />
 </xsl:if>
 <xsl:value-of select="." />
 </xsl:for-each>
 </xsl:template>

字节顺序标志以

为了控制样式表中的字节顺序标志(Byte-order-mark,BOM),可以使用 byte-order-mark Serialization 参数。要注意,允许实现者覆盖这个值。在 XSLT 1.0 中,实现可以在 UTF-8 文档中加上 BOM,UTF-16 和 UTF-32 可能也是如此。在 XSLT 1.0 中,要么实现者必须支持扩展属性,要么用户必须进行外部(转换以后)处理,以保证增加或去掉了 BOM。





回页首


结束语

本文描述了 XSLT 2.0 中最为迫切要求的主要特性,XPath 和函数库留待以后再讨论。如果还没有看到让您心甘情愿地升级到 2.0 的特性,请继续关注后续文章。



参考资料

学习

获得产品和技术
  • IBM 试用软件:用这些试用软件开发您的下一个项目,这些软件可以直接从 developerWorks 下载。


讨论


作者简介

David Marston 从 1998 年后期开始使用 XML 技术,尤其关注标准的符合性问题。在超过 25 年的计算机从业经历中,他参与了软件开发的各个方面。他毕业于达特茅斯大学,是 ACM 成员。他参加了 IBM Research 的 Next-Generation Web 团队。可以通过 David_Marston@us.ibm.com 和他联系。


Joanne Tong 是一位开发人员,在 IBM 多伦多实验室开发 IBM 的 XSLT 处理程序。她目前是 W3C XSLT 2.0 和 XQuery 1.0 Serialization 规范的编辑,XSL 工作组的一位活跃成员。可以通过 joannet@ca.ibm.com 和她联系。




对本文的评价










回页首


Unicode® 是 Unicode, Inc. 的注册商标。 其他公司、产品或服务的名称可能是其他公司的商标或服务标志。

IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款