级别: 初级 David Mertz,博士 (mertz@gnosis.cx), 推动者, Gnosis Software,Inc.
2003 年 7 月 01 日 RELAX NG 紧凑语法提供了一种格式用于描述与 RELAX NG XML 语法相同的语义约束,这种格式更简洁、更易于阅读。这一部分将探讨使用这两种语法格式并在这两者之间进行相互转换的工具。
那些阅读过我前两篇有关 RELAX NG 的文章(
第 1 部分和
第 2 部分)的读者会注意到,在我列举的许多示例中,我选择使用紧凑语法而不是 XML 语法。这两种格式在语义上是等同的,但在我看来,紧凑语法更易于阅读和编写。而且,这个专栏的读者通常会觉得我是多么不赞成这样一种概念:所有能与 XML 技术扯上关系的事物其本身也必须使用 XML 格式。XSLT 是 XML 无处不在这种趋势以及这种趋势的缺陷的主要例证 — 但这是另一专栏所要讨论的话题。
在本文的后面,我将比前两篇更详细地讨论 RELAX NG 紧凑语法的格式。
工具支持
RELAX NG 紧凑语法的不足之处在于,由于它是新鲜事物 — 它在各方面还没有 100% 定型 — 所以对该语法的工具支持没有对 XML 语法的工具支持那么全面。例如,尽管 Java 工具
trang
支持紧凑语法和 XML 语法之间的转换,但与其关联的工具
jing 只针对 XML 语法模式进行验证。显然,生成
XML 语法 RELAX NG 模式来用于验证并不十分困难,但直接使用紧凑语法模式将会更方便。类似地,Python 工具
xvif和
4xml 也只针对 XML 语法模式进行验证。
为了帮助弥补对紧凑语法的直接支持的空缺,我编写了一个 Python 工具,它可以解析 RELAX NG 紧凑模式,并将它们输出成 XML 格式。虽然我的
rnc2rng
工具仅仅实现了
trang 所完成的工作,但 Eric van der Vlist 和 Uche Ogbuji
都表示有兴趣将
rnc2rng分别包含到
xvif和
4xml 中。理想情况是,在不久的将来,针对紧凑语法模式的直接验证将包含进这两个工具。
编写
rnc2rng 实际上比我预料的要困难;这可能也是一个教训。虽然 RELAX NG
紧凑语法十分易于阅读 — 正如您将在下面所看到的 — 但实例之间标记的安排存在众多变化,这使得编写解析器不是那么简单。不论好坏,我使用
PLY 的
lex 模块来标记模式,但没有使用
yacc
进行解析,而选择了特定于应用程序的标记流的消息传递。调试声明性语法通常比用增量方式来调整命令式代码更困难。尽管我经常关注 XML 的不友好性,但解析 XML 语法模式这一任务本可以相当简单,因为我可以让 SAX 或
DOM 之类的框架来为我做大部分的解析工作。
有关 RELAX NG 编辑器的更多内容
自上篇文章以来,对 RELAX NG 的工具支持得到了一点改进。已经发布了 <oXygen/> V2.0 XML
编辑器,它以插件形式合并了
trang,所以该编辑器提供了对 RELAX NG
一些支持。虽然这里没有篇幅作全面回顾,但我发现 <oXygen/> 2.0 — 我从 V1.2 开始就喜欢上它了 — 已经具备了一些良好特性以及吸引人的地方。我愿意看到将 RELAX NG
进一步集成到各种编辑器中 — 其集成程度能达到类似于 DTD 和 W3C XML Schema
这样的程度。我想,再过一段时间,可能会有许多工具将更好地集成 RELAX NG。
语法特性:名称空间
紧凑语法 RELAX NG
模式可能以几个可选的名称空间声明中的任意一个开始。这些声明中的每一个都类似于编程语言中的赋值语句。可以用如下方式指定模式标记的缺省名称空间:
default namespace = "http://relaxng.org/ns/structure/version"
|
当转换成 XML 语法时,使用这个声明会向模式的根元素追加一个
"ns"
属性。如果没有显式地指定这个名称空间,则使用
缺省的名称空间,并将其作为根属性声明,譬如:
<root-tag xmlns="http://relaxng.org/ns/structure/1.0">
|
您也可以为元素或属性声明外部名称空间:
namespace foo = "http://some.path.to/foo"
|
这使您能够象这样描述元素:
当转换成 XML 语法时,名称空间 URL 被添加到根标记,作为根标记的另一个属性:
<root-tag xmlns="http://relaxng.org/ns/structure/1.0"
xmlns:foo="http://some.path.to/foo">
|
名称空间
"a" 在这里有点特殊。RELAX NG 允许注解(annotation),注解基本上只是带有
"a"
名称空间的标记。在紧凑语法中,您可以通过添加一个带有以两个井号标记开头的注解来避免考虑名称空间:
转换成 XML 语法时,这个注解显示为:
<a:documentation>An annotation</a:documentation>
|
顺便说一句,以单个井号开头的内容为注释(comment)而非注解,所以下面这个紧凑语法形式:
所对应的 XML 形式为:
<!-- This is a comment -->
|
您也可以使用略显奇怪的紧凑语法形式在
"a" 名称空间内指定其它注解:
[ a:defaultValue = "foo" ]
|
如果使用了注解,那么将在 XML 语法中自动指定根属性
"xmlns:a" ,但由于
"a"
正是另一个名称空间,所以如果需要,您可以指定自己的 URL。这个缺省的属性等同于指定:
namespace a = "http://relaxng.org/ns/compatibility/annotation/1.0"
|
用这两种语法形式以不同方式指定另一个特殊名称空间。数据类型取决于模块化的规范,通常使用 W3C XML Schema
数据类型。您可以用紧凑语法指定数据类型:
datatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes"
|
或者用 XML 语法指定:
<root-tag xmlns="http://relaxng.org/ns/structure/1.0"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
语法特性:嵌套和与上下文无关的样式
RELAX NG 语法的主体可以采用两种样式中的一种。在某些方面,较直接的样式是:仅将元素和属性嵌于它们应在有效实例中出现的位置。通常,使用在编程语言中所采用的缩进方式是一种好的形式,但正如在 C 家族语言中那样,花括号是实际块定界符。一般完整的模式类似于这样:
清单 1. 嵌套的紧凑语法模式
# A library patron example
default namespace = "http://some.other.url/ns"
namespace foo = "http://home.of.foo/ns"
datatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes"
## Annotation here
element patron {
element name { xsd:string { pattern = "\\w{,10}" } }
& element id-num { xsd:string }
& element book {
( attribute isbn { text }
| attribute title { text }
| attribute anonymous { empty })
}*
}
|
图书馆顾客示例用到了大多数语法元素。分布在元素(或属性)之间的
"&"
表示这几个元素必须出现,但出现的顺序可以是任意的。在 XML 语法中,这等同于
<interleave> 标记。类似地,分布于几项之间的
"|" 表示可以在其中进行选择 — 在 XML 中,与该标记相对应的是
<choice> 。还要注意
"book"
元素:圆括号表示这是一个组,但在这里它们是多余的。然而,作为量化或散置的一部分,组(XML:
<group> )很有用。例如:
清单 2. 使用组来量化
element foo {
( element bar { text },
element baz { text } )+,
element bam { text } }
|
在这种情形下,有效文档的根元素
<foo> 可能在最后一个元素
<bam> 之前包含几个
<bar></bar><baz></baz> 序列。只通过确定
"bar" 和
"baz" 元素的数量是无法表达这一概念的。
嵌套样式的 RELAX NG 语法不需要仅仅描述单个元素。任何格式良好的 XML
文档必须有一个根元素,所以很显然,在最顶层禁止使用属性。同样地,在最顶层的序列或交错描述不能描述格式良好的
XML 文档,所以它不能描述有效的文档。但允许根元素的
选项是不会有错的,譬如:
( element foo {text}
| element bar {text} )
|
第二种样式的 RELAX NG 语法更象 DTD。一开始先指出名为
"start"
的特殊
产品(production),然后紧跟各种其它指定的产品。正如使用名称空间声明一样,用编程语言的赋值方式来命名产品。例如,图书馆顾客模式也可以类似于这样:
清单 3. 与上下文无关的紧凑语法模式
# A library patron example
default namespace = "http://some.other.url/ns"
namespace foo = "http://home.of.foo/ns"
datatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes"
## Annotation here
start = patron
patron = name & id-num & book
name = element name { xsd:string { pattern = "\\w{,10}" } }
id-num = element id-num { xsd:string }
book = element book {
( attribute isbn { text }
| attribute title { text }
| attribute anonymous { empty }) }*
|
在产品中可以出现其它产品的名称,这可以防止重复,并且通常使复杂的模式更具可读性。除了可读性,命名模式还允许递归的模式定义 — 或者采用直接递归,采用相互递归。例如,不可能用严格的嵌套样式来描述 HTML — 在 HTML 中,可以在表中嵌套表,或者在列表中嵌套列表。递归的 XML 实例文档的结论是使作为描述的 DTD 和与上下文无关的 RELAX NG 比 W3C XML Schema 更自然(但您
可以通过 W3C XML Schema 获得您所需的;只是需要做更多工作)。
这里可能值得研究一个完整的 XML 语法 RELAX NG 模式文档。为了比较,清单 4 是
rnc2rng 处理清单 3 中与上下文无关的图书馆顾客模式时所生成的内容:
清单 4. 与上下文无关的 XML 语法模式
<?xml version="1.0" encoding="UTF-8"?>
<!-- A library patron example -->
<grammar xmlns="http://relaxng/ns/structure/1.0"
ns="http://some.other.url/ns"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0"
xmlns:foo="http://home.of.foo/ns">
<a:documentation>Annotation here</a:documentation>
<start><ref name="patron"/></start>
<define name="patron">
<interleave>
<ref name="name"/>
<ref name="id-num"/>
<ref name="book"/>
</interleave>
</define>
<define name="name">
<element name="name">
<data type="string"/>
<param name="pattern">\\w{,10}</param>
</data>
</element>
</define>
<define name="id-num">
<element name="id-num">
<data type="string"/>
</element>
</define>
<define name="book">
<zeroOrMore>
<element name="book">
<choice>
<attribute name="isbn"/>
<attribute name="title"/>
<attribute name="anonymous">
<empty/>
</attribute>
</choice>
</element>
</zeroOrMore>
</define>
</grammar>
|
我想说,这要比 W3C XML Schema 更具可读性,但它比紧凑语法还差很多(在前面的文章中已经指出实际上
不可能用
W3C XML Schema 或 DTD 精确地表达该模式)。
其它
在其中一些示例中,您会注意到用紧凑语法表示的元素和属性总是在其名称后面的花括号中包含
一些内容。在
XML 语法中,您可以自己关闭一个属性标记,但为了防止不确定性,对于属性主体,您至少需要指定
{text} 或
{empty} 。当然,如果您愿意,也可以使用更复杂的数据类型描述。同样,对于属性,唯一有意义的量化是
"?" — 属性可以是可选的,但它们不能多次重复。
在一些不常见的情形中,
rnc2rng不同于
trang。例如,即使在紧凑语法中注解行出现在根元素之前,这两个工具也都强制注解(在 XML 语法中)出现在根元素内。由于格式良好的 XML 文档只有一个根元素,所以这是必需的。但
trang也用类似的方式移动注释,而
rnc2rng 却不这样。还有一个小差异,这两个工具使用空格的方式略微不同。当然还可能存在其它一些不同之处,但从理论上讲,这些变化在语义方面都不重要。
参考资料
- 您可以参阅本文在 developerWorks 全球站点上的
英文原文.
- 请参加关于本文的
论坛。(您也可以通过单击本文顶部或底部的
讨论访问论坛。)
- 从
这里下载
xvif。
4Suite
这一略有名气的工具合并了用于 RELAX NG 验证的
xvif。命令行工具
4xml可以用各种选项针对 RELAX NG 和 DTD 进行验证。
4Suite 包括许多其它工具和库,这些工具和库使用了许多与
XML 相关的技术。
-
trang和
jing是用于模式间互相转换以及针对 RELAX NG 模式进行验证的补充性工具。前者依赖于后者,不过可以从
这里下载以方便的归档文件形式存在的这两个工具。
-
为了使用
trang,您将需要获取 Java API for
XML Processing(JAXP)的实现。如果您运行 Java 1.4
JVM,则没问题;否则可以从
这里下载
crimson。
-
DTDinst
是一个 Java 工具,用于将 DTD 转换成 XML 实例文档格式,包括处理参数实体。由于几乎没有别的程序使用 DTDinst,因此
DTDinstXML 格式受自身实用程序限制。然而,可以用
XSLT 样式表将该格式转换成
RELAX NG(带有一些告诫)。您将需要 XSLT 工具来利用它。
-
在这里查找这个系列文章中所提到的一组文档和工具。
- 请阅读 David Mertz 的 XML 编辑器系列:
第 1 部分探讨了 Java
和 MacOS 应用程序(包括 <oXygen/>),而
第 2 部分讨论了基于 Windows 的产品。您会在 David 的“XML 问题”专栏的
专栏汇总页面找到他以前撰写的所有文章。
- 在
developerWorks XML 专区上查找更多的 XML 参考资料。
-
IBM WebSphere Studio提供了一套使用 Java 和其它语言使 XML 开发自动化的工具。它与
WebSphere Application Server紧密集成在一起,但它也可以与其它 J2EE 服务器一起使用。
- 查找如何成为
IBM 认证的 XML 及相关技术开发人员。
关于作者
对本文的评价
|