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

发布于 2024-09-13 09:45:46 字数 867 浏览 10 评论 0原文

我想使用 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 个元素和/或无序模式,我没有好的解决方案。

I want to use RELAX NG compact syntax to validate an XML element whose children are one, two, three or n of a set of n specific elements. For instance, if the element is 'Layout' and there is a set of three specific elements: 'top', 'center' and 'bottom', the following XML element definitions would be valid:

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

<Layout>
    <top/>
</Layout>

<Layout>
    <center/>
</Layout>

<Layout>
    <bottom/>
</Layout>

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

I want to know how to write two patterns:
1) Allow the children to be in any order.
2) Restrict the children to be in a specific order (e.g.: top, center, bottom).

The solution I have so far for the XML example and the ordered pattern is:

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

I don't have a good solution for more than 3 elements and/or for an unordered pattern.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

最初的梦 2024-09-20 09:45:46

最近在 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)+ }

These was discussed recently on the RelaxNG list (I asked the question as it happens). The restriction you are trying to codify in the first case is something on the lines of "allow zero or one each of a set of elements" I think. There is no neat way to do this but you can do something. If you have a lot of possible child elements, this is fiddly to say the least (I have eight in my schema and that's enough pain honestly). Something like this should work for case 1.

I've used named patterns to make this more legible.

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) 
}

That requires any one of the elements plus zero or one of each of the other two.

In order to enforce a sequence, you can do a similar thing but use the ',' operator instead as you have done :

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?)
}

If you were going to get any more complex than this I would seriously consider writing a much simpler that simply matched the appropriate elements and then use a Schematron rule to enforce the counts. So, for your second requirement:

<?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>

or, in compact syntax (annotations are not pretty in 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)+ }
落墨 2024-09-20 09:45:46

我不确定它是否对您有帮助,但我们使用了这个。
我们的问题:两个元素,任意顺序,都可以出现 1 到 n 次
解决方案:
<交错>
<一个或更多>
>

<一个或更多>



对于订购,只需删除 。对于 0 到 n 次出现,请使用

I am not sure if it will help you but we used this.
Our problem: Two elements, any order, both can appear 1 to n amount of times
Solution:
<interleave>
<oneOrMore>
<ref name="a.class"/>
</oneOrMore>
<oneOrMore>
<ref name="b.class"/>
</oneOrMore>
</interleave>

For ordered just remove <interleave>. For 0 to n occurences use <zeroOrMore>.

浅浅淡淡 2024-09-20 09:45:46

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

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

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

You can use "element top {text}?, element center {text}?, element bottom {text}?" to get zero or one of each in the stated order

Or use "element top{text}? & element center {text}? & element bottom {text}?" to get zero or one of each in any order.

Both these patterns are extensible to arbitrarily many elements.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文