通过XSD 1.0描述具有不同出现边界的未排序元素组

时间:2016-03-24 10:26:59

标签: regex xml xsd

我试图用XSD 1.0描述以下类型的结构:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           elementFormDefault="qualified">
  <xs:element name="root">
    <xs:complexType>
      <xs:all>
        <xs:element name="A" minOccurs="1" maxOccurs="1">
        <xs:element name="B" minOccurs="1" maxOccurs="1">
        <xs:element name="C" minOccurs="1" maxOccurs="1">
        <xs:element name="D" minOccurs="0" maxOccurs="unbounded">
      </xs:all>
    </xs:complexType>
  </xs:element>
</xs:schema>

以下最小示例文档:

<?xml version="1.0" encoding="utf-8"?>
<root><D/><D/><D/><C/><D/><D/><A/><B/><D/></root>

这不起作用,因为XSD 1.0不允许<xs:all>内的元素无限制出现。由于这与(D*AD*BD*CD*|D*AD*CD*BD*|D*BD*AD*CD*|D*CD*AD*BD*|D*BD*CD*AD*|D*CD*BD*AD*)等正则表达式相对应,因此我尝试使用以下内容替换内部<xs:all> … </xs:all>

<xs:choice>
  <xs:sequence>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="A" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="B" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="C" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:sequence>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="A" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="C" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="B" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:sequence>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="B" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="A" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="C" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:sequence>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="C" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="A" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="B" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:sequence>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="B" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="C" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="A" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:sequence>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="C" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="B" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:element name="A" minOccurs="1" maxOccurs="1"/>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
</xs:choice>

但是当使用xmllint进行检查时,该工具(非常正确地)抱怨内容模型不是决定论者。有办法吗?

1 个答案:

答案 0 :(得分:2)

我们在这里有一些解决方案:

选项1:使您成为正则表达式解决方案确定者

以下是你的正则表达式转换为确定性正则表达式(请允许我使用这种正则表达式滥用符号):

d*
(
    ad*(bd*c|cd*b)
    |
    bd*(ad*c|cd*a)
    |
    cd*(ad*b|bd*a)
)
d*

转换为XSD:

<xs:sequence>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
    <xs:choice>
        <xs:sequence>
            <xs:element name="A"/>
            <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
            <xs:choice>
                <xs:sequence>
                    <xs:element name="B"/>
                    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
                    <xs:element name="C"/>
                </xs:sequence>
                <xs:sequence>
                    <xs:element name="C"/>
                    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
                    <xs:element name="A"/>
                </xs:sequence>
            </xs:choice>
        </xs:sequence>

        <xs:sequence>
            <xs:element name="B"/>
            <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
            <xs:choice>
                <xs:sequence>
                    <xs:element name="A"/>
                    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
                    <xs:element name="C"/>
                </xs:sequence>
                <xs:sequence>
                    <xs:element name="C"/>
                    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
                    <xs:element name="A"/>
                </xs:sequence>
            </xs:choice>
        </xs:sequence>

        <xs:sequence>
            <xs:element name="C"/>
            <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
            <xs:choice>
                <xs:sequence>
                    <xs:element name="A"/>
                    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
                    <xs:element name="B"/>
                </xs:sequence>
                <xs:sequence>
                    <xs:element name="B"/>
                    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
                    <xs:element name="A"/>
                </xs:sequence>
            </xs:choice>
        </xs:sequence>
    </xs:choice>
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>

选项2:使用xs:key(仅对简单内容或简单类型有效)

键应该是唯一的,始终存在(并且不可为空)。如果xs:key字段选择多个值,则文档无效,因此您可以使用:

<xs:element name="root">
    <xs:complexType>
       <xs:choice maxOccurs="unbounded">
           <xs:element name="A" type="xs:string"/>
           <xs:element name="B" type="xs:string"/>
           <xs:element name="C" type="xs:string"/>
           <xs:element name="D" type="xs:string"/>
       </xs:choice>
    </xs:complexType>
    <xs:key name="oneABC">
        <xs:selector xpath="."/>
        <xs:field xpath="A"/>
        <xs:field xpath="B"/>
        <xs:field xpath="C"/>
    </xs:key>
</xs:element>

选项3:更改XML模型以强制执行订单

不完全是一个解决方案,因为这不会验证相同的文档。如果您仅接受给定订单,这将更容易。只有在定义XML文档实例结构的情况下才能实现此目的。示例:ABCD*

<xs:sequence>
    <xs:element name="A">
    <xs:element name="B">
    <xs:element name="C">
    <xs:element name="D" minOccurs="0" maxOccurs="unbounded">
</xs:sequence>

选项4:最佳解决方案,使用XSD 1.1和xs:assert

不是一个真正的选项,因为你说你需要XSD 1.0,但万一有人可以使用XSD 1.1。 使用XSD 1.1和xs:assert

非常容易
<xs:element name="root">
    <xs:complexType>
        <xs:choice maxOccurs="unbounded">
            <xs:element name="A"/>
            <xs:element name="B"/>
            <xs:element name="C"/>
            <xs:element name="D"/>
        </xs:choice>
        <xs:assert test="count(A)=1 and count(B)=1 and count(C)=1"/>
    </xs:complexType>
</xs:element>