0

我想使用 RELAX NG 紧凑语法来验证一个 XML 元素,其子元素是一组 n 个特定元素中的一个、两个、三个或 n 个。例如,如果元素是“布局”并且有一组三个特定元素:“顶部”、“中心”和“底部”,则以下 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 个元素和/或无序模式,我没有好的解决方案。

4

3 回答 3

1

最近在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)+ }
于 2010-11-27T15:02:46.690 回答
1

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

于 2011-05-04T07:52:08.243 回答
0

您可以使用“元素顶部 {text}?,元素中心 {text}?,元素底部 {text}?” 以规定的顺序得到零个或一个

或者使用“元素顶部{text}?&元素中心{text}?&元素底部{text}?” 以任何顺序获得零个或一个。

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

于 2011-02-18T23:32:20.967 回答