级别: 中级 Uche Ogbuji (uche.ogbuji@fourthought.com), 首席顾问, Fourthought, Inc.
2004 年 10 月 01 日 如果对 XML 格式有一定的了解,并且知道不可能让与会的每个人都同意模式中的所有细节,那么请考虑 Schematron 抽象范式。Schematron 可能是最强大的 XML 模式语言(也许不仅仅是一种模式语言)。它的高级特性特别是抽象范式,允许使 XML 模式迅速适应不同的 XML 格式。这样就为 XML 模式带来了无限的可能性,包括限制 XML 格式,或者使其更具一般性和适应性。
ISO Schematron 是一种非常独特的 XML 模式语言,无论是结合其他模式语言或者单独使用都具有强大的功能。我曾经在教程“
Schematron 实用入门”中指出,Schematron 不仅仅是一种模式语言,而且是一种成熟的 XML 报告工具。本文将讨论 ISO Schematron 的一些高级特性,如果您对这种技术还不熟悉,建议您阅读那一教程。我将主要讨论变量赋值和抽象范式(pattern),这两种技术为 XML 模式设计带来了令人印象深刻的可能性。与上述那篇教程一样,本文中将使用
候选 XML表示 Schematron 模式调用的 XML 文件。
上述教程中曾经提到,严格地说, Schematron 是多种潜在的数据(包括 XML 和其他格式,如扁平文本或者数据库格式)访问方式的宿主语言。但是据我所知,所有 Schematron 执行基本上都使用 XPath 和 XSLT 作为查询语言,用于处理 XML。本文中就假定使用这样的一种执行。测试中使用的是 Scimitar ISO Schematron 工具箱(请参阅
Resources)。
Schematron 测试中的变量
Schematron 的很多思想受到 XSLT 的启发,包括使用变量简化代码。这一点对于约束数据类型非常有用,您需要验证 XML 中的某些文本是某种给定数据类型(如整数、日期、时间或 URL)的有效表示。通常,使用 XPath 检查源数据的词法特性冗长而复杂。明智地使用变量可以帮助简化。您可以按照通常的方式在
assert 和
report 元素的
test 属性中引用变量,比如
$var 。您可以在 Schematron 中使用
let 指令为变量赋值。清单 1 中的 Schematron 代码可以检验提供的钱数是否在 $10,000 和 $1,000,000 美元之间,允许使用千分符和美元符号。
清单 1. 验证钱数
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.ascc.net/xml/schematron">
<title>Example of let</title>
<pattern name="Nor too many nor too few dollars">
<rule context="money">
<!-- Remove dollar signs and US convention comma separators -->
<let name="amount" value="number(translate(@amount, '$,', ''))"/>
<assert test="$amount >= 10000 and $amount <= 1000000">
Amount should range from ten thousand to one million dollars
</assert>
</rule>
</pattern>
</schema>
|
XPath
$amount >= 10000 and $amount <= 1000000 ,相对于
number(translate(@amount, '$,', '')) >= 10000 and number(translate(@amount, '$,', '')) <= 1000000 (Schematron 与 XSLT 一样,XPath 中的“<”必须转义),当然是一种改进。
let 指令的
value 属性在运行时使用封闭的
rule 中定义的上下文计算得到。清单
2、
3和
4都是非常简单的例子,对于
清单 1来说都是有效的,清单
5和
6是无效的。
清单 2. 对清单 1 有效的代码
<money amount="$300,000.00"/>
|
清单 3. 对清单 1 有效的代码
<money amount="500,000"/>
|
清单 4. 对清单 1 有效的代码
<money amount="10000.00"/>
|
清单 5. 对清单 1 无效的代码
<money amount="$1,000.00"/>
|
清单 6. 对清单 1 无效的代码
<money amount="$3,000,000.00"/>
|
let 指令也可以出现在
rule 之外,直接作为
schema 、
pattern 或
phase 的孩子。如果
let 出现在
rule 之外,其
value 以文档根作为上下文节点计算得到。分阶段变量是参数化在不同阶段执行的测试的一种有趣方式。
Schematron 抽象范式
可以使用变量限制模式的参数化,就是说用规则之外的代码确定特定规则内的部分表达式。这种限制以如何在 XPath 中使用变量为中心。比如,如果希望参数化的是候选 XML 中的元素名,表达式可能就变得非常复杂(比方说必须使用谓词而不是简单的元素名测试)。
Schematron 抽象范式是一种更加灵活的参数化机制。抽象范式是一种特殊的范式,基本上被看作是一个模板。参数和 XPath 变量使用同样的语法(“$param”),但含义有所不同。Schematron 处理程序在用给定的值对参数进行文字串置换时,要执行一次预处理程序。抽象范式在模式编写中的重要用途在于,与候选 XML 中的精确词汇表相比,它更加灵活,强调 XML 中表达的一般概念。比方说,表格是 XML 格式中一种很常见的、增强可读性的组件。但是 XHTML 1.1 中的 Basic Table 模块和 DocBook 中的表格使用不同的词汇表。前者的词汇表源于 HTML,后者则基于 SGML 语言中的 CALS 表格标准。 清单 7 是一个抽象范式的例子,其中包含 XML 表格结构中的一些典型的约束。
清单 7. 表格中典型约束的抽象范式
<pattern abstract="true" name="table">
<rule context="$table">
<assert test="$row">A table has at least one row</assert>
</rule>
<rule context="$row">
<assert test="$cell">A table row has at least one cell</assert>
</rule>
</pattern>
|
属性
abstract="true" 表明这是一个抽象范式。范式中的查询(主要在
context 和
test 属性中)可以包含
$table 这样的
参数引用。参数引用与 XPath 变量引用非常不同,它们完全是运行之前计算的串置换,使用给定的参数可以方便地改写查询。 如果不使用参数引用,抽象范式就与非抽象范式一样。清单 8 中使用抽象范式通过提供的参数值创建它的具体版本。
清单 8. 使用抽象数据类型
<pattern name="xhtml-basic-table" is-a="table">
<param formal="table" actual="table"/>
<param formal="row" actual="tr"/>
<param formal="cell" actual="td"/>
</pattern>
<pattern name="cals-table" is-a="table">
<param formal="table" actual="ctable"/>
<param formal="row" actual="tbody/row"/>
<param formal="cell" actual="entry"/>
</pattern>
|
清单 8 创建了两个范式,由
is-a 属性给定的抽象范式的具体实例,在该例中属性值为
is-a="table" 。
param 元素提供了抽象范式中每个参数引用的值。比方说,
清单 8 中第一个
pattern 的第一个
param 具有属性
formal="table" ,表明它是为抽象范式中的参数实例
$table 赋值。这个值由
actual 属性(即
actual="table" )提供。它仅仅是置换文本,不作任何特殊处理。清单 9 是该范式的实例模型,是通过将
清单 8第二个范式的参数应用于
清单 7的抽象范式得到的。置换文本用蓝色突出显示。
清单 9. 参数已经解析的抽象范式实例模型
<pattern name="cals-table">
<rule context="
table
">
<assert test="
tbody/row
">A table has at least one row</assert>
</rule>
<rule context="
tbody/row
">
<assert test="
cell
">A table row has at least one cell</assert>
</rule>
</pattern>
|
允许使用只带一个
is-a 属性的范式指定参数,但是这种范式必须包含规则或者
let 元素。比如,如果希望通过添加范式来确保 CALS 表格中只能使用
tbody ,就不能简单地在
清单 8中的第二个范式增加一条规则,而必须为新增加的规则创建全新的、具体的范式。清单 10 是一个完整的 Schematron 例子,综合了清单
7和
8中的代码,并增加了一个新的、仅用于 CALS 表格形式的具体范式。
清单 10. 使用抽象范式的 Schematron 的完整示例
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.ascc.net/xml/schematron">
<title>Table abstract patterns</title>
<pattern abstract="true" name="table">
<rule context="$table">
<assert test="$row">A table has at least one row</assert>
</rule>
<rule context="$row">
<assert test="$cell">A table row has at least one cell</assert>
</rule>
</pattern>
<pattern name="xhtml-basic-table" is-a="table">
<param formal="table" actual="table"/>
<param formal="row" actual="tr"/>
<param formal="cell" actual="td"/>
</pattern>
<pattern name="cals-table" is-a="table">
<param formal="table" actual="ctable"/>
<param formal="row" actual="tbody/row"/>
<param formal="cell" actual="entry"/>
</pattern>
<!-- special stand-alone pattern for CALS-specific table rules -->
<pattern name="cals-table-extra">
<rule context="ctable">
<assert test="tbody">A table has a tbody</assert>
</rule>
</pattern>
</schema>
|
结束语
您已经看到,Schematron 抽象范式的基本机制非常简单。虽然如此,它却大大增强了 XML 模式的表达能力。常常遇到这样的情况,就模式的基本想法能够广泛达成一致,但是很难统一到具体的语法上来。比方说,表示订单可能有无数种 XML 格式,但是都需要有一些品名元素、交货地点等。 Schematron 抽象范式可以独立于订单格式所用的具体语法描述这类一般性概念。这种灵活性是 SGML 中的 Architectural Form 规范的优点之一。Architectural Form 是创建特殊 DTD 的一种方法,这种 DTD 允许 XML 名称的再映射。这种方法非常巧妙,但是也很复杂,很少有人能够充分地理解它并利用其强大的功能。Schematron 将其用于 XPath 查询从而提供了一种更加简单、更加灵活的方法。
如我在“
技巧:将数据词典用于 XML 和 Web 服务大纲”一文中所述,如果再加上语义丰富的注释,还可以进一步增强 Schematron 抽象范式的表达能力。得到的模式更容易适应不同的语法,同时又保持
语义透明性,这是我在
Thinking XML
专栏中发明的一个词,即这样一种能力,无论信息使用了何种具体的语法,XML 系统都能正确解释。
即使眼下还用不上语义透明性,抽象范式也是一种很有用的工具。如果 XML 词汇表的用户分布在世界各地,而您希望提供元素名和属性名、受控词汇表等的本地化版本,可以考虑使用抽象范式。总之,这种 Schematron 设备将让您重新考虑更好的 XML 模式设计方式。
参考资料
关于作者
对本文的评价
|