这些元素中至少有一个,但最多只有一个

时间:2014-03-05 14:30:55

标签: xsd

我希望我的XSD能够强制执行必须存在一组元素中的至少一个。其中一些元素可以无限次出现,有些元素是有限的。

这些元素本身并不是强制性的,但至少必须存在一个元素。

最后,元素可以按任何顺序出现。即该命令既不应强制执行,也不应对文件的处理产生任何影响

我的尝试:

<xs:element name="Root">
  <xs:complexType>
    <xs:choice>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="A" type="a:a"/>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="B" type="a:a"/>
      <xs:element minOccurs="0" maxOccurs="1" name="C" type="a:a"/>
      <xs:element minOccurs="0" maxOccurs="1" name="D" type="a:a"/>
    </xs:choice>
  </xs:complexType>
</xs:element>

这不太有用,因为它不会强制执行这些元素中的至少一个。

因此,在上面的示例中,必须至少有A,B,C或D中的一个。 可以有多个A或B元素,但每个C或D只有1个

所以以下内容都应该有效:

  • <root><A/></root>
  • <root><A/><A/></root>
  • <root><A/><B/><A/></root>
  • <root><C/></root>
  • <root><D/><C/></root>

但以下内容不应有效:

  • <root/> - 必须至少有一个元素
  • <root></root> - 必须至少有一个元素
  • <root><C/><C/></root> - 重复<C/>元素

这是一个额外的复杂性,这是现有complexType的扩展,但我认为这不会影响任何事情?

这可能吗?

我尝试将minOccurs =“1”添加到xs:choice元素中。这没有任何影响,大概是因为选择的每个实例都可能是空的!

我也尝试过同时使用xs:choice(子节点中没有minOccurs)和xs:sequence(没有C&amp; D节点)但是我找不到任何允许两者共存的父元素

1 个答案:

答案 0 :(得分:1)

除了允许前两个应该是无效的示例之外,您显示的内容模型也因为不允许第三个或第五个应该是正确的示例而失败。

到目前为止,捕获您在XSD,Relax NG或DTD中描述的约束的最简单方法是添加另一个约束并限制订单。然后,内容模型(以DTD表示法)((A+, B*, C?, D) | (B+, C?, D?) | (C, D?) | (D))。如果A,B,C,D序列没有传达任何信息,这是迄今为止最简单的方法。

如果孩子的序列确实传达了信息,并且需要自由,那么你就有了一项最复杂的任务。不是特别困难,但有时复杂(特别是对于更复杂的例子)相当乏味。

您描述的A,B,C和D儿童的合法序列是一种常规语言;考虑用于识别它的有限状态自动机可能会有所帮助。从本质上讲,您需要跟踪三位信息:(a)我们是否看到过任何子元素? (b)我们见过C元素吗? (c)我们看过D元素了吗?如果这些完全独立,我们在FSA中需要八个州;因为它们不是,我们只需要五个:

  • 0开始状态(不,不,不)
  • 1已经看到一个或多个A或B元素但不是C或D(是,否,否)
  • 2看过C元素,没有D(X,是,否)
  • 3见过D元素,没有C
  • 4见过C和D

转换表是

  • 0 A,B - &gt; 1; C - &gt; 2; D - &gt; 3
  • 1 A,B - &gt; 1; C - &gt; 2; D - &gt; 3
  • 2 A,B - &gt; 2; C - &gt;错误; D - &gt; 4
  • 3 A,B - &gt; 3; C - &gt; 4; D - &gt;错误
  • 4A,B - &gt; 4; C,D - &gt;错误

接受状态为1-4。

请注意,一旦我们看到至少一个元素,A和B都不会对自动机的状态产生任何影响。这表明开始编写内容模型的好方法:暂时忽略A和B并编写仅包含C和D的内容模型。由于其中一个必须发生,但两者都不能重复,我们可以描述C的合法序列和D元素这种方式(再次,DTD符号表示紧凑性):

((C, D?) | (D, C?))

允许A和B任意多次出现(但不是在序列的开头)给我们

( (C, (A|B)*, (D, (A|B)*)?)
| (D, (A|B)*, (C, (A|B)*)?) )

如果我们从A或B开始,As和Bs的初始序列可以跟随与上述序列匹配的任何序列,或者什么都不是。对于这种情况,内容模型将是

((A|B)+, ( (C, (A|B)*, (D, (A|B)*)?)
         | (D, (A|B)*, (C, (A|B)*)?) )? )

将这些内容放在一起,内容模型整体成为

( ( (A|B)+, ( (C, (A|B)*, (D, (A|B)*)?)
            | (D, (A|B)*, (C, (A|B)*)?) )? )
| (C, (A|B)*, (D, (A|B)*)?)
| (D, (A|B)*, (C, (A|B)*)?) )

将此转换为XSD表示法很简单。

在某些模式语言中,有更方便(通常是人们的意思:更紧凑)表达此内容模型或相关内容模型的方式。

在Schematron或XSD 1.1中,您可以编写断言(使用XPath)来说明

  • 每个孩子都被命名为A,B,C或D.
  • 至少有一个孩子。
  • 最多只有一个名叫C的孩子。
  • 最多只有一个名为D的孩子。

在XSD 1.1中,你可以使用一个全组,其子组为A *,B *,C?和D ?,以及一个断言,表明至少有一个孩子。

在Relax NG中,您可以编写一个允许空序列的内容模型,但是使用interleave运算符进行描述,因此:

(A* & B* & C? & D?)

您可以使用稍微复杂的模型强制执行非空虚:

( ((A|B), (A* & B* & C? & D?))
| (C, (A* & B* & D?))
| (D, (A* & B* & C?)) )