【问题标题】:XML Schema Design: restrict the occurrence of an element according to its position in the documentXML Schema Design:根据元素在文档中的位置限制元素的出现
【发布时间】:2014-05-25 00:09:52
【问题描述】:

您好,我遇到了一个关于根据元素在文档中的位置限制元素出现的问题。实际上“位置”可能不是一个合适的术语,但我想不出更好的方法来总结这个问题。 无论如何,让我解释一下业务逻辑。该模式将被设计用于播放脚本。在一出戏中,一个演员可以扮演多个角色。一出戏包含许多场景,在一个场景中,有多个舞台方向(进入/退出)出现,在两个舞台方向之间,演员进行演讲。示例 xml 实例如下所示:

<PLAY>
    <CAST>
        <ROLE>
            <ACTOR id="1">Alex</ACTOR>
            <PERSONA>Superman</PERSONA>
        </ROLE>
        <ROLE>
            <ACTOR id="1">Alex</ACTOR>
            <PERSONA>Batman</PERSONA>
        </ROLE>
        <ROLE>
            <ACTOR id="2">John</ACTOR>
            <PERSONA>Hulk</PERSONA>
        </ROLE>
    </CAST>
    <TITLE>Lego Movie</TITLE>
    <SCENE>
        <TITLE>SCENE I</TITLE>
        <STAGEDIR>Enter Superman, Hulk</STAGEDIR>
        <SPEECH>
            <SPEAKER actorID="1">Superman</SPEAKER>
            <LINE>Hahaha</LINE>
        </SPEECH>
        <SPEECH>
            <SPEAKER actorID="2">Hulk</SPEAKER>
            <LINE>Hahaha</LINE>
        </SPEECH>
        <STAGEDIR>Exit Superman, Enter Batman</STAGEDIR>
        <SPEECH>
            <SPEAKER actorID="1">Batman</SPEAKER>
            <LINE>Yo</LINE>
        </SPEECH>
        <SPEECH>
            <SPEAKER actorID="2">Hulk</SPEAKER>
            <LINE>Yo</LINE>
        </SPEECH>
    </SCENE>
</PLAY>

这里需要的限制是,如果一个演员扮演多个角色,则不允许两个角色同时在舞台上互相交谈(如果一个演员扮演多个角色,一个演员的角色不能同时出现在舞台上)。比如 Alex 同时扮演超人和蝙蝠侠,他们不能同时出现在舞台上,因为他们是由同一个人扮演的。 目前架构如下:

<xs:complexType name="PLAYTYPE">
    <xs:sequence>
        <xs:element ref="CAST"/>
        <xs:element ref="TITLE"/>
        <xs:element ref="SCENE"/>
    </xs:sequence>
</xs:complexType>

<xs:complexType name="CASTTYPE">
    <xs:sequence>
        <xs:element ref="ROLE" maxOccurs="unbounded"/>
    </xs:sequence>
</xs:complexType>

<xs:complexType name="ROLETYPE">
    <xs:sequence>
        <xs:element ref="ACTOR"/>
        <xs:element ref="PERSONA"/>
    </xs:sequence>
</xs:complexType>

<xs:complexType name="SCENETYPE">
    <xs:sequence>
        <xs:element ref="TITLE"/>
        <xs:element ref="STAGEDIR"/>
        <xs:element ref="SPEECH" maxOccurs="unbounded"/>
    </xs:sequence>
</xs:complexType>

<xs:complexType name="ACTORTYPE">
    <xs:sequence>
        <xs:element ref="NAME"/>
    </xs:sequence>
    <xs:attributeGroup ref="attlist.ACTOR"/>
</xs:complexType>

<xs:complexType name="STAGEDIRTYPE" mixed="true">
    <xs:sequence>
        <xs:element minOccurs="1" maxOccurs="unbounded" ref="PERSONA"/>
    </xs:sequence>
    <xs:attributeGroup ref="attlist.SPEAKER"/>
</xs:complexType>

<xs:complexType name="SPEECHTYPE">
    <xs:sequence>
        <xs:element ref="SPEAKER"/>
        <xs:element ref="LINE"/>
    </xs:sequence>
    <xs:attributeGroup ref="attlist.ACTOR"/>
</xs:complexType>

<xs:complexType name="SPEAKERTYPE" mixed="true">
    <xs:attributeGroup ref="attlist.SPEAKER"/>
</xs:complexType>

<xs:element name="PLAY" type="PLAYTYPE"/>
<xs:element name="CAST" type="CASTTYPE"/>
<xs:element name="ROLE" type="ROLETYPE"/>
<xs:element name="ACTOR" type="ACTORTYPE"/>
<xs:element name="SCENE" type="SCENETYPE"/>
<xs:element name="STAGEDIR" type="STAGEDIRTYPE"/>
<xs:element name="SPEAKER" type="SPEAKERTYPE"/>
<xs:element name="TITLE" type="xs:string"/>
<xs:element name="PERSONA" type="xs:string"/>
<xs:element name="NAME" type="xs:string"/>
<xs:element name="LINE" type="xs:string"/>

<xs:attributeGroup name="attlist.ACTOR">
    <xs:attribute name="id" use="required"/>
</xs:attributeGroup>

<xs:attributeGroup name="attlist.SPEAKER">
    <xs:attribute name="actorID" use="required"/>
</xs:attributeGroup>

那么如何在schema中实现这种限制呢?

【问题讨论】:

  • 我认为架构定义是错误的逻辑层来定义这种业务逻辑。它不是对可以表示哪种数据的限制,而是对将哪些数据应用于现实世界时有意义的限制。
  • 您的意思是在同一个&lt;SCENE&gt; 中不能有多个&lt;SPEAKER&gt; 元素具有相同的id 和不同的内容?这可以通过 XPath 断言来实现,但您必须升级到 XSD 1.1。
  • @helderdarocha 感谢您的回复。一个 中允许有多个 元素,不允许的是两个 元素之间具有相同“actorId”的多个 元素。比如第一个舞台方向,演员上台,亚历克斯同时扮演超人和蝙蝠侠,所以超人和蝙蝠侠不能出现在同一个舞台方向。但是在第二阶段方向,超人退出阶段,现在蝙蝠侠可以进入。昨晚我正在检查 XSD 1.1 中的断言,我将尝试通过使用它来实现这一点。谢谢。
  • @IMSoP 我也这么认为。但这是我目前最终面临的一个实际问题。我阅读了您的解决方案和其他一些使用 Assertion 和 XPath 来完成类似任务的示例,我将尝试看看是否可以使用它们来完成。谢谢。
  • 我明白了。我注意到 &lt;STAGEDIR&gt; 实际上接受 &lt;PERSONA&gt; 对象。如果这些&lt;PERSONA&gt; 对象是参与场景的角色,并且如果它们可以具有actorID 属性,则可以在&lt;STAGEDIR&gt; 内部进行一致性检查,而不必检查preceding:: 节点的麻烦对于场景中的演讲者。

标签: xml xsd schema-design


【解决方案1】:

要强制规定对于每个&lt;SCENE&gt;,任何具有相同actorID 属性的&lt;SPEAKER&gt; 元素都必须包含相同的文本,您可以使用XPath 2.0 断言声明您的SCENETYPE,如下所示:

<xs:complexType name="SCENETYPE">
    <xs:sequence>
        <xs:element ref="TITLE"/>
        <xs:element ref="STAGEDIR"/>
        <xs:element ref="SPEECH" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:assert test="every $speakerId in SPEECH/SPEAKER/@actorID
                       satisfies
                        (every $speaker in SPEECH/SPEAKER[@actorID=$speakerId]
                          satisfies 
                            (SPEECH/SPEAKER[@actorID=$speakerId])[1] = $speaker)"/>
</xs:complexType>

这适用于支持&lt;xs:assert&gt;XSD 1.1。您还可以使用 XSD 1.0 的 Schematron 扩展来执行类似的操作。

有了这个限制,这个块就会生效,因为有一个一致的映射:1 = Superman2 = Hulk

<SCENE>
    ...
    <SPEECH id="1">
        <SPEAKER actorID="1">Superman</SPEAKER>
        <LINE>Hahaha</LINE>
    </SPEECH>
    <SPEECH id="2">
        <SPEAKER actorID="2">Hulk</SPEAKER>
        <LINE>Hahaha</LINE>
    </SPEECH>
    <SPEECH id="3">
        <SPEAKER actorID="1">Superman</SPEAKER>
        <LINE>Yo</LINE>
    </SPEECH>
    <SPEECH id="4">
        <SPEAKER actorID="2">Hulk</SPEAKER>
        <LINE>Yo</LINE>
    </SPEECH>
</SCENE>

但这会验证失败,因为1 包含SupermanBatman

<SCENE>
    <TITLE>SCENE I</TITLE>
    <STAGEDIR actorID="0"><PERSONA>Superman</PERSONA><PERSONA>Hulk</PERSONA><PERSONA>Batman</PERSONA></STAGEDIR>
    <SPEECH id="1">
        <SPEAKER actorID="1">Superman</SPEAKER>
        <LINE>Hahaha</LINE>
    </SPEECH>
    <SPEECH id="2">
        <SPEAKER actorID="2">Hulk</SPEAKER>
        <LINE>Hahaha</LINE>
    </SPEECH>
    <SPEECH id="3">
        <SPEAKER actorID="1">Batman</SPEAKER>
        <LINE>Yo</LINE>
    </SPEECH>
    <SPEECH id="4">
        <SPEAKER actorID="2">Hulk</SPEAKER>
        <LINE>Yo</LINE>
    </SPEECH>
</SCENE>

如果您仍在设计此架构,更好的选择是检查您的 &lt;STAGEDIR&gt; 元素中的这种一致性,因为 可能有相同的演员在如果它在不同的时刻退出并返回,则相同的场景。由于&lt;PERSONA&gt; 可以由不同的演员扮演,例如,您可能希望有一些方法来关联currentActorId,当您在&lt;STAGEDIR&gt; 中列出&lt;PERSONA&gt; 元素时,您可以在那里检查一致性,而不是检查整个场景。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-28
    • 2011-02-05
    • 1970-01-01
    • 2021-09-07
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    • 1970-01-01
    相关资源
    最近更新 更多