6

我正在尝试以任何顺序列出允许的元素列表。一些元素是必需的(最小值为 1,最大值为 1),一些是可选的,最多为 1,而一些是可选的,具有任意数量。这就是我所拥有的,并且 XSD 是有效的,但是当我去验证 XML 时,我试图实现的规则没有被强制执行。例如,id 不是必需的。

<xsd:complexType name="feedType">
        <xsd:annotation>
            <xsd:documentation>
                The Atom feed construct is defined in section 4.1.1 of the format spec.
            </xsd:documentation>
        </xsd:annotation>
        <xsd:choice minOccurs="3" maxOccurs="unbounded">
            <xsd:element name="author" type="atom:personType" minOccurs="0" maxOccurs="unbounded"/>
            <xsd:element name="category" type="atom:categoryType" minOccurs="0" maxOccurs="unbounded"/>
            <xsd:element name="contributor" type="atom:personType" minOccurs="0" maxOccurs="unbounded"/>
            <xsd:element name="generator" type="atom:generatorType" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="icon" type="atom:iconType" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="id" type="atom:idType" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="link" type="atom:linkType" minOccurs="0" maxOccurs="unbounded"/>
            <xsd:element name="logo" type="atom:logoType" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="rights" type="atom:textType" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="subtitle" type="atom:textType" minOccurs="0" maxOccurs="1"/>
            <xsd:element name="title" type="atom:textType" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="updated" type="atom:dateTimeType" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="entry" type="atom:entryType" minOccurs="0" maxOccurs="unbounded"/>
            <xsd:any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
        </xsd:choice>
        <xsd:attributeGroup ref="atom:commonAttributes"/>
    </xsd:complexType>
4

3 回答 3

11

我刚刚遇到了同样的问题,并查看了他们在XHTML XSD中做了什么。里面的情况相同head:title是必需的,是可选的,base然后允许任意数量的script, style,和元素。metalinkobject

以下是他们所做的:首先,他们将可能多次出现的可选元素分组:

  <xs:group name="head.misc">
    <xs:sequence>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element ref="script"/>
        <xs:element ref="style"/>
        <xs:element ref="meta"/>
        <xs:element ref="link"/>
        <xs:element ref="object"/>
      </xs:choice>
    </xs:sequence>
  </xs:group>

head然后他们在实际定义中引用了这个组:

  <xs:sequence>
    <xs:group ref="head.misc"/>
    <xs:choice>
      <xs:sequence>
        <xs:element ref="title"/>
        <xs:group ref="head.misc"/>
        <xs:sequence minOccurs="0">
          <xs:element ref="base"/>
          <xs:group ref="head.misc"/>
        </xs:sequence>
      </xs:sequence>
      <xs:sequence>
        <xs:element ref="base"/>
        <xs:group ref="head.misc"/>
        <xs:element ref="title"/>
        <xs:group ref="head.misc"/>
      </xs:sequence>
    </xs:choice>
  </xs:sequence>

这有点棘手。写成类似正则表达式的伪代码,上面看起来像这样:

misc=(script|style|meta|link|object)*
head=${misc}(title${misc}(base${misc})?|base${misc}title${misc})

所以,从技术上讲,它是有效的。

但是,这需要将强制和可选元素的所有可能排列(中间有杂项)放入choice. 对于n元素,这就是n!子节点。因此,对于 XHTML,n=2他们最终以n!=2. 在你的情况下n=8,它将是n!=40320

FWIW,这是生成 XSD 的算法:

result = '<xs:group name="misc"><xs:sequence><xs:choice minOccurs="0" maxOccurs="unbounded">'
forall(optionalArbitraryCountElements as element)
    result += '<xs:element ref="' + element.name + '"/>'
result += '</xs:choice></xs:sequence></xs:group>'

result += '<xs:complexType name="feedType"><xs:sequence><xs:group ref="misc"/><xs:choice>'
permutations = getAllPermutations(mandatoryElements + optionalOnceElements)
foreach (permutations as p)
    result += '<xs:sequence>'
    foreach (p as element)
        if (element.isOptional)
            result += '<xs:sequence minOccurs="0">'
        result += '<xs:element ref="' + element.name + '"/><xs:group ref="misc"/>'
        if (element.isOptional)
            result += '</xs:sequence>'
    result += '</xs:sequence>'
result += '</xs:choice></xs:sequence></xs:complexType>'

return result
于 2013-02-12T22:35:11.500 回答
5

choice只允许其子元素之一出现在 XML 图中。sequence如果您的元素始终处于相同的顺序,您似乎想要使用它。如果顺序是可变的,那么您应该使用all并包装maxOccurs="unbounded"包含列表元素中的所有元素,因为all只允许其子元素出现 1 次或 0 次。

编辑:

您应该从选择元素中删除 minOccurs 和 maxOccurs。这仅允许您强制执行 3 个选项,但不允许您指定它们是什么选项(包括多次重复相同的元素)。我不知道你到底想强制执行什么,但它不会以这种方式有效执行。

编辑2:

您可以按如下方式创建列表包装器(以链接元素为例):

<xs:element name="linkList" minOccurs="0" maxOccurs="1">
<xs:complexType>
  <xs:sequence>
    <xs:element name="link" type="atom:linkType" minOccurs="0" maxOccurs="unbounded" />
  </xs:sequence>
</xs:complexType>
</xs:element>

在 xml 文档中,您将(例如)将链接元素嵌套在链接列表中:

老的:

<other elements...>
<id>...</id>
<link>...</link>
<link>...</link>
<logo>...</logo>
<other elements...>

新的:

<other elements...>
<id>...</id>
<linkList>
  <link>...</link>
  <link>...</link>
</linkList>
<logo>...</logo>
<other elements...>
于 2012-06-05T18:16:29.827 回答
2

基本上,在使用 XSD 时不要这样做。它不是为这样的用例而设计的。

您可以改用 RelaxNG进行验证,使用其无序操作,或者如果您有需要 XSD 的合作伙伴,则强制执行任意顺序。

出于这个原因, RFC4287中给出的原子提要模式使用了 RelaxNG:

   atomFeed =
      element atom:feed {
         atomCommonAttributes,
         (atomAuthor*
          & atomCategory*
          & atomContributor*
          & atomGenerator?
          & atomIcon?
          & atomId
          & atomLink*
          & atomLogo?
          & atomRights?
          & atomSubtitle?
          & atomTitle
          & atomUpdated
          & extensionElement*),
         atomEntry*
      }

有关何时在 XML 中强制执行顺序的进一步讨论,请参阅XML 设计原则:当 XML 元素的顺序很重要时

于 2016-04-06T10:52:11.277 回答