内容


准备从 XSLT 1.0 升级到 2.0,第 3 部分

为什么需要制定迁移计划

样式表编写者需要在遗留样式表中修改哪些内容

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 准备从 XSLT 1.0 升级到 2.0,第 3 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:准备从 XSLT 1.0 升级到 2.0,第 3 部分

敬请期待该系列的后续内容。

关于本系列教程

W3C 发布的最新规范 XSLT 2.0 是一种转换 XML 文档的语言。它包括很多新的特性,其中一些特性是专为克服 XSLT 1.0 的不足而设计的。这组文章从 XSLT 1.0 用户的角度(希望解决老问题、学习新技术、发现值得期待的新特性),提供了对 XSLT 2.0 的高水平概述和深入剖析。我们提供了来自常见应用程序的例子,并为那些希望升级的用户提供了实用的建议。为了帮助您开始使用 XSLT 2.0,本文还提供了相应的迁移技术。

XSLT 处理程序的遵从性

实现 XSLT 2.0 的软件供应商必须遵循 W3C 发布的规范(请参阅 参考资料 中的链接),但是允许有所不同。和 XSLT 1.0 一样,很多细节是事先定义的,即每种实现可以选择怎么做。XSLT 2.0 也有三个互相独立的主要模块,供应商可以选择实现额外的特性:序列化、模式感知和向后兼容。供应商实现的每个特性模块必须符合规范,虽然模块中还有一些地方允许供应商自己选择。对于序列化特性,2.0 的遵从性要求比 1.0 更清晰,在 1.0 中不仅序列化本身是可选的,而且很多特征也是可选的(表达方式是 应该 而不是 必须)。

2.0 中减少了允许供应商选择的细节,主要是要求构造结果中出现的某些错误必须标记为错误(而不允许改变结构得到意外的结果)。仍然有很多实现定义的内容,可以在 XSLT 2.0 规范的附录中找到完整的 XSLT 选项列表。其中部分实现定义的选项允许供应商支持特定的语言和地区。在描述每种特性模块的章节之后还可读到更多相关内容。

需要向后兼容吗?

向后兼容(BC) 特性的主要目标是允许能够用于 1.0 处理程序的样式表也能(或多或少)地用于 2.0 处理程序。符合 2.0 的处理程序不是和 1.0 规范 100% 兼容的,因此和调用旧的 1.0 代码不同。它仍然是一个 2.0 处理程序!不应该将其看作一个问题,某些情况下也可能是一个机会(允许在两种方式中选择一个)。本系列文章旨在介绍跨版本兼容性的各个方面。

您所熟悉的 XSLT 1.0 在设计时就考虑到将来会制定新的版本。最重要的证据是要求所有的样式表在外层 xsl:stylesheet 元素中使用 version 属性。所有 XSLT 处理程序包括 1.0 处理程序,都要求支持向前兼容(Forwards Compatibility,FC),就是说如果样式表的版本号高于处理程序的版本,处理程序应该能够处理它能识别的部分。如果要让 FC 在样式表中生效,处理程序必须对未知的属性、已知属性的未知值、XSLT 声明和指令更加宽松。还必须忽略新的 XSLT 元素而不引起错误。但是要注意,少数情况下,1.0 中存在的指令在 2.0 中不是通过属性扩展的。您可能在现有的样式表中使用了 FC 以便为 2.0 做准备,不过请坚持阅读本系列指南。

2.0 处理程序可能仅支持 XSLT 2.0(和 FC),没有实现 BC 特性。这意味着样式表中出现的任何 version="1.0" 都会造成错误,除非在 xsl:output 中用于设置 XML 的版本。本系列的 第 2 部分 提出了整体切换到 2.0 的一些决策因素,这一部分将进一步分析这些信息。如果支持 BC 特性,处理程序不仅接受顶层 xsl:stylesheet 元素中的 version="1.0",而且允许在下级元素中出现,这种情况下它作用于该元素及其后代。遗留样式表审查表 中列出了一些方法,限制 BC 的作用范围可以解决两个版本行为上的一些差异。

需要模式感知吗?

模式感知是一些符合 XSLT 2.0 的处理程序支持的另一种可选特性。如果支持该特性,那么附加的语法项及其在 XSLT 转换上的作用就是经过定义的和可互操作的。该特性主要用于错误检查。这是一种内建在语言中的工具,可以让样式表编写者验证临时(变量)或最终状态的原子值和节点的模式类型。还可用于从输入源树和临时树中选择特定模式类型的节点,在 XML Schema 规范(请参阅参考资料)定义的内置原子类型以外创建原子值,用 instance of 运算符确认节点和原子值的类型,以临时或最终形式创建模式验证的节点。本系列 第 1 部分 中,表 1 介绍了 10 种可使用模式感知的语法项。

不要认为只有当使用 XML 模式时才需要该特性。在转换过程中,只有当需要验证和选择类型化的节点或者使用用户定义的模式类型时才需要模式(不论来自外部文件还是内嵌在样式表中)。在不使用模式的情况下,如果要处理内置的原子类型如 xs:nonNegativeIntegerxs:token,仍然需要该特性。在样式表中遇到这些类型时,非模式感知的处理程序将抛出类型错误。如果只需要处理类型化的原子值,而且这些值只需要用更常见的内置模式类型验证,比如 xs:floatxs:boolean,那么就不需要模式感知特性。关于非模式感知处理程序支持的完整类型列表,请参阅 XSLT 2.0 规范 3.13 节(要获得 W3C 网站上的 XSLT 2.0 链接,请参阅 参考资料)。

需要序列化吗?

序列化是符合规范的 XSLT 处理程序不一定要支持的一个可选特性,尽管有些人可能感到意外。多数处理程序至少支持序列化特性的一个子集。如果 XSLT 2.0 处理程序声明说完全支持该特性,则必须实现 xsl:outputxsl:character-map 声明中定义的所有属性,并且能够序列化到 XML、HTML、XHTML 和文本输出文件(或者输出方法如字节流)。

允许处理程序扩展序列化功能。因此,处理程序可以支持自定义输出方法、扩展属性来控制序列化的某些方面,或者支持已有属性的新值(只要规范允许)。如果想了解基本的序列化需求是否能满足您的需要,请重新审查已有的样式表和可能的转换后处理机制。此外,还要看看 XSLT 2.0 处理程序供应商提供的文档,了解是否存在能够代替已有 1.0 扩展机制的序列化扩展,从而简化向 XSLT 2.0 的迁移。还要看看文档中关于 XML 1.1、规范化形式(如 NFD)和 @disable-output-escaping 的支持,因为这也是基本序列化特性需求的一部分。不要仅仅因为不支持 @disable-output-escaping 而否定一种处理程序。XSLT 2.0 不赞成使用该特性,用户可容易地使用对 xsl:character-map 的标准支持来代替它(关于字符映射的更多信息,请参阅本系列的 第 1 部分)。

买方注意:处理程序可能还有其他变化

XSLT 处理程序的区别不仅在于是否提供上述的模块特性,还可能在更小的附加特性上,处理程序供应商可以自行决定某些方面。许可的变化可能意味着一种实现在报告错误方面更严格,而另一种实现可能修复(如果允许的话)而不是抛出错误。下面详细介绍可能对遗留样式表的迁移影响较大的一些事先定义的方面。

仍然需要扩展机制吗?

很多 XSLT 处理程序公开了接口或者提供这样的方法,用于在特定应用程序框架中编写的应用程序中作为一个组件来执行。一些处理程序可能还支持扩展设施(在应用程序框架所用的编程语言中),允许转换引擎在执行过程中识别和处理扩展函数和指令。比如流行的开放源代码 XSLT 1.0 处理程序 Xalan-J 支持一种扩展机制,允许在样式表中实例化 Java 对象和调用 Java 方法。为了将 XPath 表达式的值映射到 Java 方法的参数或者返回值,Xalan-J 提供了 XSLT 类型到 Java 类型的映射。比如,样式表中变量引用的结果树片段在 Java 方法被作为 org.w3c.dom.DocumentFragment 对象。如果 1.0 样式表使用了这类扩展设施,首先要分析扩展代码执行的功能能否用 XSLT 2.0 和 XPath 2.0 所提供的新特性代替。比如说,如果实现了 F&O(函数和运算符规范,请参阅 参考资料)不支持的 random() 函数,就要看看 XSLT 2.0 处理程序是否提供了能够最大限度地减少迁移到该处理程序的工作量的扩展设施。

EXSLT 是一种伪标准,提供了 XSLT 1.0 缺少的功能扩展。很多现有的 1.0 处理程序对 EXSLT 扩展提供了有选择的支持。虽然很多扩展和 F&O 库重复或这很容易用 XSLT 2.0 语法重写,但某些扩展,如 evaluate()script,需要处理程序在标准 XSLT 2.0 之外提供兼容的支持。某些 XSLT 处理程序提供的另一类扩展允许连接到 SQL 数据库和检索数据。如果样式表没有这些扩展就不能工作,请查看供应商提供的文档。

需要命名空间轴吗?

在 XSLT 2.0 中对命名空间轴(即 namespace::nodetest)的支持是可选的。因此,如果 1.0 样式表大量使用了命名空间轴,而又不想改成调用 in-scope-prefixes()namespace-uri-for-prefix(),就要看看 XSLT 2.0 处理程序上的文档是否默认(以非 BC 模式运行)支持命名空间轴。如果准备利用 BC,就必须支持命名空间轴。

适应不同的环境

其他变化包括:

  • 支持不同 URI 系统和 URI 中的片段标识符。它影响 xsl:import、xsl:include、doc()unparsed-text() 等。
  • 支持 XML 1.1。对输入的源文档没有影响,但是如果希望序列化到 XML 1.1 文档或者样式表是 XML 1.1 文档,则需要这种支持。
  • 支持各种字符集、字符串的规范化形式和校验。
  • 和语言有关的数字和日期、校验以及对不同历法的支持。
  • 支持的小数精度应超过最低要求(包括时间和时期值)。
  • trace() 函数表示的信息类型。
  • 处理错误和警告的方式,包括 error() 函数做什么。比如,如果特定结构的执行不可能成功,处理程序可以在执行模板前将类型错误作为静态错误抛出。
  • collection() 函数必须返回一个节点序列,多次调用同一个函数必须返回同样的结果,但除此以外具体的功能完全由实现定义。

如果上述问题对您来说非常重要,那么在选择 2.0 处理程序的时候一定要考虑它们。

启动转换的机制

为了在应用程序中启动 XSLT,多数应用程序开发环境都公开了定制的接口和定制的方法,或者支持 JAXP (Java API for XML Processing) 这样的标准转换 API 来执行转换。撰写本文的时候,还没有支持 XSLT 2.0 的标准接口。不过,除非使用标准 API,或者希望指定新的启动选项来执行转换,或重定向其他的结果(使用 xsl:result-document@href),否则这不是个大问题。况且,处理程序供应商很可能在选择的应用程序语言中提供初始化处理的某些支持。新的 XSLT 2.0 启动选项能够设置:

  • 初始模式
  • 初始命名模板
  • 初始上下文节点
  • 基准输出 URI

现有的标准 API 还提供了设置样式表参数值的方法。如果输入源是一个节点(比如使用 DOM Source),那么也可以设置初始上下文节点。需要注意的是,不能将 BC 模式设置为启动选项,只有样式表的内容才能触发该模式。

检查 XSLT 遗留代码,确定需要 2.0 处理程序提供什么功能

XSLT 样式表仍然类似程序的一种特殊形式,从外部来看 2.0 中大部分都没有变。但是很多方面有所改进,因为代码更加清楚直接。(关于这一点的例子请参阅本系列的第 1 部分。)希望下表能够帮助您了解需要修改的某些方面,还说明了如何修改以及可能影响处理程序选择的一些允许的差异。也可帮助您从 1.0 迁移到 2.0。

表 1. 可能影响 2.0 处理程序选择的 XSLT 特性
在 1.0 中使用的特性或者希望用 XSLT 达到的目标 最简单的 2.0 解决方案对选择 2.0 处理程序的影响

使用命名模板执行一般操作,如:

  1. 全部字母改为大写
  2. 检查一个长字符串是否以某个短字符串结束
  3. 发现集合中不重复的值
  4. 将数字四舍五入到某个小数位数(不能是零)

    以及其他一般操作。

    特别需要注意的是命名模板使用递归

  5. 计算一组连续的整数
  6. 计算数字的最小值和最大值
  7. 按照分隔字符分解字符串

    以及其他一般操作。

  8. 层层穿过节点集得到一个布尔值。

新增的函数和 XPath 能够解决大部分这些问题。针对给出的示例,可以使用以下解决方案:

  1. upper-case()
  2. ends-with()
  3. distinct-values()
  4. round-half-to-even()
  5. 使用 to 运算符的范围表达式
  6. min()max()
  7. tokenize()
  8. 限定表达式(XPath 3.9 节)

如果没有针对您的任务的内置函数,那么可使用 xsl:function 来定义您自己的函数。

BC 模式允许进行某些自动类型转换和自动选择节点集的第一个成员。特别要指出的是,这适用于函数参数,这样用户就可使用更多的函数。

假设您希望马上调用一个命名模板。可以在启动转换时传递样式表参数,来确定先调用哪个模板。

2.0 把选择初始命名模板作为一个新的启动选项,但是仍然允许传递样式表参数。(要注意:样式表参数不是作为初始命名模板的参数传递的!)

指定初始模板 启动转换 的具体方法取决于具体的实现。

在样式表中大量使用全局变量或模板参数作为通信工具。特别要注意:接收一些参数但是不做处理而传递给其他模板的模板。

如果不断向模板堆栈中传递值,就意味着可能需要使用通道参数。如果需要,隐含文档节点(IDN)可以携带整个值结构,减少了传递的参数个数。

在 2.0 中传递多余的参数(被调用模板中没有声明的输入参数)会导致错误,但 BC 模式会排除该错误。

当需要模板参数的时候,依赖于不需编写的内置 match-pattern 模板或使用 xsl:apply-imports 在多个模板处理中运行相同的代码。

现在可以在 xsl:apply-imports 上使用 xsl:with-param。这些模板参数和以前一样没有被抛弃。最好使用明确定义的模板而不要依赖于内置模板。可以通过多个模板(包括内置模板)传递通道参数,而不会丢失参数。

无。

在指令序列中使用仅引用一次的局部变量。需要设置变量是因为(可能在命名模板中)使用了一系列“编程”指令来设置它的值。或者设置局部变量然后使用 xsl:for-each 来为内部计算改变视点。

IfExpr、RangeExpr、ForExpr 等新增加的函数大大减少了通过编程指令计算值的需要。ForExpr(XPath 3.7 节)解决了视点改变的问题。

BC 模式允许进行某些自动类型转换和自动选择节点集的第一个成员。具体来说,可用于函数参数,而且有更多的函数可用。

2.0 中的数据类型更加正规,可能要求进行分析,模式的使用增加了仔细检查的必要性。

使用 node-set() 扩展函数使结果树片段(RTF)能够导航和筛选。

直接使用 RTF,不用管隐含文档节点(IDN)。关于 IDN 的更多信息,请参阅 第 1 部分

无。

使用扩展函数(节点集除外)。通过扩展函数或命名模板来模拟数据类型(除了 XPath 1.0 类型以外,如 dateTime)。日期和时间值通常作为字符串处理,但如果需要加上时期值则作为数字处理。

新的函数和 XPath 能力满足了很多需要。

关于新的日期/时间/时期功能,请参阅本系列的 第 1 部分

BC 模式也允许某些自动类型转换。特别适用于函数参数。

模式感知可以帮助管理自定义的数据类型。

使用极大或极小的数字或者模仿从数值类型派生的私有数据类型。

使用显示的数据类型控制和新函数可以避免陷入困境。注意这些数字用科学计数法显示的情况。

检查供应商的文档看看事先定义的精度是否达到了最低要求。

BC 模式对数字比较的影响有两个方面:允许选择集合的第一个成员,对于非数字值返回 NaN 而不是抛出类型错误。详情参阅 XPath 3.4 节。

2.0 中的数据类型更加正规,可能要求进行分析,模式的使用增加了仔细检查的必要性。

小心处理字符串以便支持特定语言/国家的需要,尤其是对排序和显示而言。

使用改进的 2.0 功能,避免使用 disable-output-escaping 属性。

  • format-date() 函数
  • format-time() 函数
  • format-dateTime() 函数
  • xsl:number 中的格式
  • xsl:sort 中可以进行更好的控制
  • default-collation 属性

检查供应商文档对实现定义的地区支持和排列支持。有些供应商允许自己编写排序器。

应该仔细检查序列化,特别是对编码的支持。

很多希望接收一个数字或者原子字符串的函数和运算符依赖于 1.0 的行为,如果传递的参数是一个节点集,就会自动选择节点集的第一个成员并提取其原子值。

例外包括 =、!=、<、<=、>、>=、id()、 boolean()、not()

如果允许选择多个节点,可使用 xsl:value-of @select="some node"

如果不能使用 BC 模式,对于返回多个节点的节点集表达式可增加谓词 [1]

第 1 部分曾经提到,xsl:value-of(2.0 中)将自动迭代值的序列,包括节点集。

如果处理程序支持 BC 模式,使用局部版本化启用 version="1.0"。(和寻找需要 [1]的所有地方相比,这样更简单。)BC 模式影响 xsl:value-of 的迭代功能。

当匹配模式或者进行值比较时,需要检查数据类型。比如,要保证比较的两个值是数字而不是字符串。

使用 instance of 运算符。

阅读 XPath 2.0 规范的 3.5 节了解 eq 关系的严格用法。

新的匹配模式可能造成模板具有相近的默认优先级。如果这样的话就明确地设置优先级。

使用 element()attribute() 模式的双参数形式。

BC 模式对数字比较的影响有两个方面:允许选择集合的第一个成员,对于非数字值返回 NaN 而不是抛出类型错误。详情参阅 XPath 3.4 节。

模式感知允许 instance of 根据模式中定义的所有类型确认节点的类型。也允许 schema-element()schema-attribute() 节点测试。模式感知影响的语法项请参阅 第 1 部分 中的表 1。

可以让选择排列生效,影响关系运算符 eq、le 等。

包含原子值(数字、布尔值或字符串)的变量和另一个此类变量或者文字原子值比较,希望一种类型自动转换成另一种类型。以及向面向字符串的函数如 substring()、contains() 传递非字符参数等。

例子:

contains($MyNumber, '0')

从输入文档中取回的值都转化成 xs:untypedAtomic,从而能够进行比较或者传递给函数,但是如果两个操作数都标记了明确且不同的类型,比较的时候就会造成类型错误。将一个操作数包装到 number()、boolean()、string() 或者其他新增的类型构造器函数中。

如果函数需要字符串,则用 string() 包装参数。

如果支持 BC 特性,可以设置 version 1.0 让它在表达式求值的元素中生效,这样就不用担心类型匹配问题了。

模式感知允许更具体的类型转换。如果使用模式感知而且输入文档通过了模式验证,标记为 2.0 新增类型(xs:date、xs:duration、xs:hexBinary 等)或者从这些基本类型派生的私有类型就不能自动转换成字符串。可以手工转换或者(最好)编写新的使用这些类型的表达式。

查找第一个分组键并作为组成分组的触发器来对元素分组。常用的形式如下:

  • generate-id(key(...))
  • preceding-sibling axis
  • xsl:for-each select="key(...)"

使用 xsl:for each-group(可能代替 xsl:for-each),请参阅本系列 第 1 部分 的说明。

无。

直接操作源文档中的命名空间节点并希望控制输出中的命名空间声明。通过使用命名空间轴或者复制元素来得到其命名空间来访问这样的节点。在输出端,可以创建一个属性以它的名义添加命名空间声明。

注意:2.0 处理程序可以发现语法无效的 Namespace URI。

使用 xsl:namespace 创建命名空间节点。

了解处理命名空间、QNames 和 URI 的新函数,特别是本文命名空间轴一节提出的两条建议。不通过命名空间轴,使用新的函数应该也能检索需要的信息。

如果序列化 XML,一定要注意命名空间修正。需要新的 URI 来代替那些无效的 URI。

查看供应商文档了解 2.0 处理程序是否支持命名空间轴,因为在 2.0 中是可选的特性。支持 BC 要求支持命名空间轴。

如果处理程序能够创建 XML 1.1,可以取消带前缀的命名空间声明。

关于命名空间的解释请参阅参考资料

使用导入或包含样式表,尤其是如果确实需要条件包含的话。

问问自己:为何要划分模块?其中一些是工具模板吗?要获得关于模板使用条件的信息(无论是否要导入/包含),请查看 use-when

如果使用 http 之外的 URI 方案,请查看供应商文档中提取下层样式表的方法。

BC 允许混合使用 1.0 和 2.0 模块。比如,2.0 主样式表可以包含 1.0 样式表。

XSLT 2.0 处理程序是全新的

即使您是无意中使用了 2.0 的行为,2.0 处理程序仍将按照 2.0 规范的要求操作。2.0 文档没有按通常的方式结合 1.0;全部内容都从零开始重新定义,包括向后兼容行为。支持 BC 特性的 2.0 处理程序将在 BC 模式下运行 1.0 样式表,但并没有忽略 xsl:for-each-group 这样的新指令。阅读本系列的其他部分、2.0 规范和其他资料的时候要记住这一点。

升级到 2.0 将使递归的使用更少、RTF 更少、随意的参数传递更少,以及 1.0 中其他类似的不愉快更少。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=192998
ArticleTitle=准备从 XSLT 1.0 升级到 2.0,第 3 部分: 为什么需要制定迁移计划
publish-date=01302007