最近在RelaxNG 列表上讨论了这些问题(我问了这个问题)。在第一种情况下,您试图编纂的限制是我认为“允许一组元素中的每个元素为零或一个”的内容。没有巧妙的方法可以做到这一点,但你可以做点什么。如果您有很多可能的子元素,那么至少可以说这是繁琐的(我的架构中有八个,老实说,这已经足够痛苦了)。像这样的东西应该适用于案例 1。
我使用了命名模式来使其更清晰。
start = e.layout
e.top = element top { text }
e.center = element center { text }
e.bottom = element bottom { text }
e.layout = element Layout {
(e.top & e.center? & e.bottom?) |
(e.top? & e.center & e.bottom?) |
(e.top? & e.center? & e.bottom)
}
这需要任何一个元素加上零或其他两个元素中的每一个。
为了强制执行序列,您可以执行类似的操作,但使用 ',' 运算符,就像您所做的那样:
start = e.layout
e.top = element top { text }
e.center = element center { text }
e.bottom = element bottom { text }
e.layout = element Layout {
(e.top, e.center?) |
(e.top, e.center, e.bottom?) |
(e.top, e.center, e.bottom) |
(e.top, e.bottom?)
}
如果您要变得比这更复杂,我会认真考虑编写一个更简单的,只需匹配适当的元素,然后使用 Schematron 规则来强制计数。因此,对于您的第二个要求:
<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
xmlns:sch="http://purl.oclc.org/dsdl/schematron">
<start>
<ref name="e.Layout"/>
</start>
<define name="e.top">
<element name="top">
<text/>
</element>
</define>
<define name="e.center">
<element name="center">
<text/>
</element>
</define>
<define name="e.bottom">
<element name="bottom">
<text/>
</element>
</define>
<define name="e.Layout">
<sch:pattern name="check no more than one of each">
<sch:rule context="Layout/*">
<sch:assert test="count(../*[local-name(.) eq local-name(current())]) = 1">You may only have one <name/> element as a child of Layout.</sch:assert>
</sch:rule>
</sch:pattern>
<element name="Layout">
<oneOrMore>
<group>
<ref name="e.top"/>
<ref name="e.center"/>
<ref name="e.bottom"/>
</group>
</oneOrMore>
</element>
</define>
</grammar>
或者,在紧凑的语法中(注解在 rnc 中并不漂亮):
namespace sch = "http://purl.oclc.org/dsdl/schematron"
start = e.Layout
e.top = element top { text }
e.center = element center { text }
e.bottom = element bottom { text }
[
sch:pattern [
name = "check no more than one of each"
"\x{a}" ~
" "
sch:rule [
context = "Layout/*"
"\x{a}" ~
" "
sch:assert [
test = "count(../*[local-name(.) eq local-name(current())]) = 1"
"You may only have one " rng:name [ ] " element as a child of Layout"
]
"\x{a}" ~
" "
]
"\x{a}" ~
" "
]
]
e.Layout = element Layout { (e.top, e.center, e.bottom)+ }