使用RELAX NG紧凑语法对XML元素的子节点进行有序/无序定义

时间:2010-08-05 17:41:10

标签: xml xsd relaxng

我想使用RELAX NG紧凑语法来验证一个XML元素,其子元素是一组n个特定元素中的一个,两个,三个或n个。例如,如果元素是'Layout'并且有一组三个特定元素:'top','center'和'bottom',则以下XML元素定义将是有效的:

<Layout>
    <top/>
    <center/>
    <bottom/>
</Layout>

<Layout>
    <top/>
</Layout>

<Layout>
    <center/>
</Layout>

<Layout>
    <bottom/>
</Layout>

<Layout>
    <top/>
    <center/>
</Layout>

我想知道如何编写两种模式: 1)让孩子们处于任何秩序。 2)限制儿童按特定顺序(例如:顶部,中部,底部)。

到目前为止,我对XML示例和有序模式的解决方案是:

element Layout {
   (
      element top { text },
      element center { text }?
   ) |(
      element top { text }?,
      element center { text }?,
      element bottom { text }
   ) |(
      element center { text }
   )
}

对于超过3个元素和/或无序模式,我没有一个好的解决方案。

3 个答案:

答案 0 :(得分:1)

最近在RelaxNG list讨论了这些问题(我问过这个问题)。在第一种情况下,你试图编纂的限制是“我认为允许零或一组元素中的每一个”。没有简洁的方法可以做到这一点,但你可以做点什么。如果你有很多可能的子元素,那至少可以这么说(我的模式中有八个,而且说实话就足够了)。这样的事情应该适用于案例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)+ }

答案 1 :(得分:1)

我不确定它是否会对你有帮助,但我们使用了它。 我们的问题:两个元素,任何顺序,都可以出现1到n次 解: <interleave> <oneOrMore> <ref name="a.class"/> </oneOrMore> <oneOrMore> <ref name="b.class"/> </oneOrMore> </interleave> 如需订购,只需删除<interleave>即可。对于0到n次出现,请使用<zeroOrMore>

答案 2 :(得分:0)

您可以使用“element top {text} ?, element center {text} ?, element bottom {text}?”按所述顺序获得零或一个

或使用“element top {text}?&amp; element center {text}?&amp; element bottom {text}?”以任何顺序获得零或一个。

这两种模式都可以扩展到任意多种元素。