XSD唯一元素XPath的限制

3
为了允许一个元素出现多次,但限制元素值只能被允许一次,我会应用“unique”元素。由于XSD 1.0中XPath的限制,我只能以低效的方式实现它。不幸的是,对我来说,XSD 1.1不是一个选项。
以下是XSD的简化版本:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">  
    <xs:simpleType name="DeliveryMethod">
        <xs:restriction base="xs:string">
            <xs:enumeration value="POST"/>
            <xs:enumeration value="DIGITALARCHIVE"/>
        </xs:restriction>
    </xs:simpleType>

    <xs:complexType name="Mailpack">
        <xs:sequence>
            <xs:element maxOccurs="unbounded" name="deliveryMethod" type="DeliveryMethod"/>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="Letter">
        <xs:complexContent>
            <xs:extension base="Mailpack">
                <xs:sequence>
                    <xs:element name="content" type="xs:string"/>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:element name="batch">
        <xs:complexType>
            <xs:sequence>
                <xs:element minOccurs="0" name="id" type="xs:string"/>

                <xs:choice>
                    <xs:element maxOccurs="unbounded" name="letter" type="Letter">
                        <xs:unique name="oneOfEachDeliveryMethodType">
                            <xs:selector xpath="deliveryMethod"/>
                            <xs:field xpath="." />
                        </xs:unique>
                    </xs:element>
                </xs:choice>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

使用以下XML:

<?xml version="1.0" encoding="utf-8"?>
<batch>
    <id>123</id>
    <letter>
        <deliveryMethod>POST</deliveryMethod>
        <deliveryMethod>DIGITALARCHIVE</deliveryMethod>
        <deliveryMethod>DIGITALARCHIVE</deliveryMethod>
        <content>Narf!</content>
    </letter>
</batch>

这很顺利,但是却给我带来了“DIGITALARCHIVE”重复出现的错误。 问题在于这种方法强制我在每个可能的扩展Mailpack类型中包含unique元素。 所以如果我例如将一个Parcel类型引入作为Mailpack的扩展,则必须从新的batch/parcel元素中复制相同的unique元素,并将其放到batch/letter元素中。 将unique元素纳入deliveryMethod元素并不是一个选项,因为在XSD XPath中不允许使用父级选择器..
<xs:complexType name="Mailpack">
    <xs:sequence>
        <xs:element maxOccurs="unbounded" name="deliveryMethod" type="DeliveryMethod">
            <xs:unique name="oneOfEachDeliveryMethodType">
                <xs:selector xpath="deliveryMethod"/>
                <xs:field xpath=".." />
            </xs:unique>
        </xs:element>
    </xs:sequence>
</xs:complexType>

将其直接作为batch元素的一部分会导致相同的问题。
如何只包含一次unique元素,但使其对Mailpack的所有扩展都计数?(如果可能的话)

我猜你不能使用XSD 1.1,这是真的吗? - sergioFC
@sergioFC:恐怕XSD 1.1不是一个选项。我很想使用它,但即使过了3年,一个重要的应用程序仍然不支持它。 - Forage
我能想到的唯一替代方案是将<deliveryMethod>列表包装在<MailPack>元素中。 - sergioFC
@sergioFC:那会有什么区别吗?你会把“unique”元素放在哪里? - Forage
在包含 <deliveryMethod> 列表的元素内。请注意,我在我的答案中将其称为 <deliveryMethodsList> 而不是 <MailPack>。 - sergioFC
我的替代方案对你来说可行吗? - sergioFC
1个回答

2
通过将<deliveryMethod>元素包装在(例如)<deliveryMethodsList>元素中,您可以移动unique元素,这样您只需要编写一次。
例如,您只需要用以下内容替换Mailpack complexType:
<xs:complexType name="Mailpack">
    <xs:sequence>
        <xs:element name="deliveryMethodsList">
            <xs:complexType>
                <xs:sequence>
                    <xs:element maxOccurs="unbounded" name="deliveryMethod" type="DeliveryMethod"/>
                </xs:sequence>
            </xs:complexType>
            <!-- Unique is only placed here -->
            <xs:unique name="oneOfEachDeliveryMethodType">
                <xs:selector xpath="deliveryMethod"/>
                <xs:field xpath="." />
            </xs:unique>
        </xs:element>
    </xs:sequence>
</xs:complexType>

现在你不需要在letters列表中使用unique元素:

<xs:element maxOccurs="unbounded" name="letter" type="Letter" />

现在下面的示例无效,因为<deliveryMethod>DIGITALARCHIVE</ deliveryMethod>出现两次:
<?xml version="1.0" encoding="utf-8"?>
<batch>
    <id>123</id>
    <letter>
        <deliveryMethodsList>
            <deliveryMethod>POST</deliveryMethod>
            <deliveryMethod>DIGITALARCHIVE</deliveryMethod>
            <deliveryMethod>DIGITALARCHIVE</deliveryMethod>
        </deliveryMethodsList>
        <content>Narf!</content>
    </letter>
</batch>

非常好用。将deliveryMethod元素包装在单独的元素中,而不是直接在letter元素中“浮动”,使其更加清晰。谢谢! - Forage
我很高兴它起作用了。我不确定这是否符合您的需求。不客气。 - sergioFC
你介意自己更正一下帖子中的DeliveryMethodsListdeliveryMethodsList吗?管理人员拒绝了我的编辑请求,并错误地表示它不会提高准确性。现在出现了不一致的样式和引用。 - Forage
当然,你是对的,现在它更好并且已经被修正了。 - sergioFC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接