Это обсуждалось недавно в списке 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)+ }