regex - 通过 XSD 1.0 描述具有不同出现范围的未排序元素组

标签 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:让您的正则表达式解决方案成为决定论

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

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>

关于regex - 通过 XSD 1.0 描述具有不同出现范围的未排序元素组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36197964/

相关文章:

regex - Ansible 正则表达式替换 Ubuntu netplan 文件中的 DNS 地址

c# - 从 C# 中的字符串中提取 5 位数字的最快方法是什么

javascript - 转义 Javascript 字符串,未终止的字符类错误

java - Flying Saucer 无法识别 html 实体

android RadioButton 图片和文字

xml - 为什么我们需要 targetNamespace?

XML Schema 类型别名?

c# - RegEx.Match 不返回预期的字符串

c++ - Sonar 无法读取我的 CPPUnit 报告

java - 在 Java 中修改 XML 的内存有效方法