【问题标题】:Xml schema - define child elements 0-* in any orderXML 模式 - 以任何顺序定义子元素 0-*
【发布时间】:2010-08-31 08:50:44
【问题描述】:

我想定义一个 xml 模式,其中元素连接器有 0-* 个子元素。任意顺序的序列、关联或消息,从 0 到多次。即

<Connectors>
    <Sequence />
    <Association />
    <Message />
    <Sequence />
    <Sequence />
    <Message />
    <Message />
    <Association />
</Connectors>

我尝试定义以下架构,但似乎顺序是固定的。

<xs:element name="Connectors">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="Association" minOccurs="0" maxOccurs="unbounded" />
            <xs:element ref="Message" minOccurs="0" maxOccurs="unbounded" />
            <xs:element ref="Sequence" minOccurs="0" maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>
</xs:element>

【问题讨论】:

    标签: xml xsd


    【解决方案1】:

    我通过设置 choice 并设置 minOccurs 和 maxOccurs 属性解决了这个问题。

    <xs:element name="Connectors">
        <xs:complexType>
            <xs:choice minOccurs="0" maxOccurs="unbounded">
                <xs:element ref="Association" />
                <xs:element ref="Message" />
                <xs:element ref="Sequence" />
            </xs:choice>
        </xs:complexType>
    </xs:element>
    

    【讨论】:

    • 清晰而合乎逻辑的答案 (+1)。尽管我添加了自己的答案,但实际上我更喜欢您的答案。我的回答只是为了表明只有一个属性才能使您的原始代码执行您想要的操作。
    【解决方案2】:

    @Torbjörn 自己的答案比这个更清楚,但您最初的尝试只需稍作改动就会导致相同的结果:在 &lt;xs:sequence&gt; 元素上添加 maxOccurs="unbounded"

    <xs:element name="Connectors">
        <xs:complexType>
            <xs:sequence maxOccurs="unbounded">
                <xs:element minOccurs="0" name="Association" />
                <xs:element minOccurs="0" name="Message" />
                <xs:element minOccurs="0" name="Sequence" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    

    确保随机顺序,因为每个元素都有minOccurs="0",并且序列可以一遍又一遍地重复。

    【讨论】:

      【解决方案3】:

      (这不完全是对 OP 的回答,而是对 OP 问题的另一个建议答案的回答。作为一个全新的成员,我不确定这是否违反了 stackoverflow.com 上的一些规则或约定。抱歉。 )

      正如用户@Richard 在他的回答中所写:

      不幸的是,在 XML Schema 中没有办法定义“这组子元素以任何顺序但每个至少出现一次”。

      您被固定的顺序或一组中的一个(可能重复)所困。

      没有标签或属性特别允许这种结构是正确的,但是引用的文本在理论上不是正确的,尽管随着元素数量的增加,解决方案很快就会变得庞大。为这种包含多个元素的结构编写模式非常费力、容易出错且难以维护。因此,即使存在解决方案,实际实施也不可能也不值得。

      原始海报问题只有一组随机出现的三个元素。正确的解决方案很简单,因为没有一个元素是强制性的。但即使在这种情况下,使用这么少的元素集,强制每个元素至少出现一次仍然比较容易实现。下面的代码示例

      <?xml version="1.0" encoding="UTF-8"?>
      <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
          <!-- Schema for 3 elements with random order.
              Each element must appear at least once. -->
      
          <xs:element name="Association" type="xs:string"/>
          <xs:element name="Message" type="xs:string"/>
          <xs:element name="Sequence" type="xs:string"/>
      
          <xs:element name="root">
              <xs:complexType>
                  <xs:sequence>
                      <xs:element maxOccurs="unbounded" name="Connectors" type="unordered-3-group" />
                  </xs:sequence>
              </xs:complexType>
          </xs:element>
      
          <xs:complexType name="unordered-3-group">
          <!-- state level 0 -->
              <xs:group ref="Connectors-state-0"/>
          </xs:complexType>
      
          <xs:group name="Connectors-state-0">
              <xs:sequence>
                  <!-- Empty, no previous elements
                  <xs:choice minOccurs="0" maxOccurs="unbounded">
                  </xs:choice> -->
                  <xs:choice>
                      <xs:sequence>
                          <xs:element ref="Association"/>
                          <xs:group ref="Connectors-state-1a"/>
                      </xs:sequence>
                      <xs:sequence>
                          <xs:element ref="Message"/>
                          <xs:group ref="Connectors-state-1b"/>
                      </xs:sequence>
                      <xs:sequence>
                          <xs:element ref="Sequence"/>
                          <xs:group ref="Connectors-state-1c"/>
                      </xs:sequence>
                  </xs:choice>
              </xs:sequence>
          </xs:group>
      
          <xs:group name="Connectors-state-1a">
              <xs:sequence>
                  <xs:choice minOccurs="0" maxOccurs="unbounded">
                      <xs:element ref="Association"/>
                  </xs:choice>
                  <xs:choice>
                      <xs:sequence>
                          <xs:element ref="Message" />
                          <xs:group ref="Connectors-state-2a"/>
                      </xs:sequence>
                      <xs:sequence>
                          <xs:element ref="Sequence" />
                          <xs:group ref="Connectors-state-2b"/>
                      </xs:sequence>
                  </xs:choice>
              </xs:sequence>
          </xs:group>
      
          <xs:group name="Connectors-state-1b">
              <xs:sequence>
                  <xs:choice minOccurs="0" maxOccurs="unbounded">
                      <xs:element ref="Message"/>
                  </xs:choice>
                  <xs:choice>
                      <xs:sequence>
                          <xs:element ref="Association" />
                          <xs:group ref="Connectors-state-2a"/>
                      </xs:sequence>
                      <xs:sequence>
                          <xs:element ref="Sequence" />
                          <xs:group ref="Connectors-state-2c"/>
                      </xs:sequence>
                  </xs:choice>
              </xs:sequence>
          </xs:group>
      
          <xs:group name="Connectors-state-1c">
              <xs:sequence>
                  <xs:choice minOccurs="0" maxOccurs="unbounded">
                      <xs:element ref="Sequence"/>
                  </xs:choice>
                  <xs:choice>
                      <xs:sequence>
                          <xs:element ref="Association" />
                          <xs:group ref="Connectors-state-2b"/>
                      </xs:sequence>
                      <xs:sequence>
                          <xs:element ref="Message" />
                          <xs:group ref="Connectors-state-2c"/>
                      </xs:sequence>
                  </xs:choice>
              </xs:sequence>
          </xs:group>
      
          <xs:group name="Connectors-state-2a">
              <xs:sequence>
                  <xs:choice minOccurs="0" maxOccurs="unbounded">
                      <xs:element ref="Association"/>
                      <xs:element ref="Message"/>
                  </xs:choice>
                  <xs:choice>
                      <xs:sequence>
                          <xs:element ref="Sequence" />
                          <xs:group ref="Connectors-state-3a"/>
                      </xs:sequence>
                  </xs:choice>
              </xs:sequence>
          </xs:group>
      
          <xs:group name="Connectors-state-2b">
              <xs:sequence>
                  <xs:choice minOccurs="0" maxOccurs="unbounded">
                      <xs:element ref="Association"/>
                      <xs:element ref="Sequence"/>
                  </xs:choice>
                  <xs:choice>
                      <xs:sequence>
                          <xs:element ref="Message" />
                          <xs:group ref="Connectors-state-3a"/>
                      </xs:sequence>
                  </xs:choice>
              </xs:sequence>
          </xs:group>
      
          <xs:group name="Connectors-state-2c">
              <xs:sequence>
                  <xs:choice minOccurs="0" maxOccurs="unbounded">
                      <xs:element ref="Message"/>
                      <xs:element ref="Sequence"/>
                  </xs:choice>
                  <xs:choice>
                      <xs:sequence>
                          <xs:element ref="Association" />
                          <xs:group ref="Connectors-state-3a"/>
                      </xs:sequence>
                  </xs:choice>
              </xs:sequence>
          </xs:group>
      
          <xs:group name="Connectors-state-3a">
              <xs:sequence>
                  <xs:choice minOccurs="0" maxOccurs="unbounded">
                      <xs:element ref="Association"/>
                      <xs:element ref="Message"/>
                      <xs:element ref="Sequence"/>
                  </xs:choice>
                  <!-- Empty, no new elements
                  <xs:choice>
                      <xs:sequence>
                      </xs:sequence>
                  </xs:choice> -->
              </xs:sequence>
          </xs:group>
      
      </xs:schema>
      

      Connectors 就像 OP 一样用作容器。单独的root 元素允许多个Connectors,只是为了更容易测试不同的组合。

      用组元素模仿不同的状态

      我已编辑此答案并更新了代码。新的更长,但它是利用基于状态的模式设计的一个非常明显的例子。代码遵循可以验证此问题的有限状态机的不同阶段。所有状态都表示为&lt;xs:group&gt; 元素,每个组引用代表所有可能的后续状态的其他组。所有&lt;xs:group&gt; 元素的结构都遵循相同的规则。这些组有两个选择的序列。第一个&lt;xs:choice&gt; 是已经看到的元素的无限重复。第二个&lt;xs:choice&gt; 包含所有可能的新元素的序列,这些元素在对表示下一个状态的组的引用之后。

      问题的复杂性

      可以使用具有 2^N 个不同状态(包括初始状态)的确定性有限状态机来验证 N 个元素的情况。状态将形成 N 个不同的级别,每个级别上的状态数量对应于二项式系数,即不同级别上的状态数量为“n over k”,其中“n”表示不同元素的数量,“k”表示级别的深度。因为再添加一个元素会使所需状态的数量增加一倍,所以&lt;xs:group&gt;s 的数量也会增加一倍,因此这种复杂类型的 LOC 大致会增加一倍。

      这个有什么用吗?

      也许……也许不是。为大型元素集手动编写尤其是更新这种结构可能是不合理的。然而,由于所有组都具有相似的结构以编程方式为整体生成适当的 XML Schema 代码应该相对容易complexType结构。其中一种方法是我的头脑是一个函数,它接受两个列表作为参数,看到和未看到的元素,然后将所有看到的元素添加到第一个 &lt;xs:choice&gt; 元素,并为每个未看到的元素创建一个到适当新状态的转换,然后为每个新引用的元素调用自身状态。

      请记住,即使生成代码消除了手动跟踪整个结构的痛苦,但在大型元素集上,代码大小会变得如此之大,以至于大小不再合理。一组 14 个元素需要 2^14 = 16383 个组,在深度级别 7 处,最多有(14 over 7)= 3432 个并行状态组,每个组有 39 个元素(= 略高于 40 LOC)。不,你不想要那样的东西。在这种情况下,您应该开始寻找其他合适的架构定义语言。

      【讨论】:

        【解决方案4】:

        这是一个非常古老的线程,但如果这可以帮助这里的任何人,我就是这样做来解决问题的。使用 xsd:all 并在任何可以省略的子项上设置 minOccurs="0":

        <xs:element name="Connectors">
            <xs:complexType>
                <xs:all>
                    <xs:element name="Association" minOccurs="0"/>
                    <xs:element name="Message" minOccurs="0"/>
                    <xs:element name="Sequence"  minOccurs="0"/>
                </xs:all>
            </xs:complexType>
        </xs:element>
        

        【讨论】:

          【解决方案5】:

          不幸的是,在 XML Schema 中没有实用的方法来定义“这组子元素以任何顺序但每个都至少出现一次”。

          您被固定的顺序或一组中的一个(可能重复)所困。

          编辑:没有实际的方法,手动生成每个可能的序列是可能的并且会起作用,但是组合爆炸将很快失控。

          【讨论】:

          • 请参阅我的其他答案,了解这实际上是如何实现的,尽管实际上无法在较大的元素集上实现。
          • @Jasso: 手动声明每一个可能的序列... 有趣 :-)
          • 是的,在某种程度上,这就像从佛罗里达步行到纽约。如果你真的需要,你可以做到。但说真的......使用其他方式旅行(或者在这种情况下,使用其他语言来定义模式)更容易;-)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-23
          • 1970-01-01
          • 2011-10-09
          • 2014-11-19
          • 2012-05-30
          相关资源
          最近更新 更多