根据xsd中的位置更改公共元素的可用属性

时间:2014-06-05 14:43:16

标签: xml xsd

我在xsd中使用复杂的公共类型,并且我想根据元素的位置(xpath)更改其中一个属性的可能值。

我目前正在使用枚举集来定义可能的值,但是如果元素出现在xml文档中的一个特定位置,我想为此属性提供3个可能的值,如果它出现在其他任何位置,我希望有2个可能的值。

是否可以使用xsd强制执行此约束?我知道我可以定义2个单独的元素,但是有问题的元素包含许多子元素和属性,我只需要对一个特定属性进行更改。

我们使用的验证引擎只是Schema 1.0 complient。

2 个答案:

答案 0 :(得分:0)

您可以使用 XSD 1.1 ,在那里您可以选择一些选项。对于元素,一个是type alternatives,但由于测试是基于 context 并且需要在父轴上进行XPath测试,因此它不起作用,因为XSD实际上支持XPath子集,不允许这样的比赛。

但您可以在层次结构中较高的类型中使用<xs:assert>,并使用XPath限制子节点中的属性和值。

假设你有一个像这样的元素:

<xs:element name="anElement">
    <xs:complexType>
        <xs:attribute name="a1">
            <xs:simpleType>
                <xs:restriction base="xs:string">
                    <xs:enumeration value="one"/>
                    <xs:enumeration value="two"/>
                    <xs:enumeration value="three"/>
                </xs:restriction>
            </xs:simpleType>
        </xs:attribute>
    </xs:complexType>
</xs:element>

该元素可能出现在rootchild元素中:

<xs:element name="child">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="anElement"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>

<xs:element name="root">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="anElement"/>
            <xs:element ref="child" />
        </xs:sequence>
    </xs:complexType>
</xs:element>

并且您要建立一条规则,例如:“如果<anElement><root>的孩子,则@a属性可能包含值one或{{ 1}}。如果它是two的孩子,那么这些值只能是<child>two

您可以通过在three元素声明的上下文中添加<xs:assert>来强制执行该规则:

<root>

然后像这样的情况有效

<xs:element name="root">
    <xs:complexType>
        <xs:sequence>...</xs:sequence>
        <xs:assert test="(anElement/@a1='one' or anElement/@a1='two') and 
                         (child/anElement/@a1='two' or child/anElement/@a1='three')"/>
    </xs:complexType>
</xs:element>

但是这会在<root> <anElement a1="one"/> <child> <anElement a1="three"/> </child> </root>

的两个实例中失败验证
<anElement>

答案 1 :(得分:0)

如果您只想避免两次写出多个子元素和属性,请使用命名组(并参阅this Stack Overflow question)。

如果对于您来说重要的是所讨论的元素在类型系统中明确相关,事情会变得更复杂,但这种技术非常值得学习。

一种方法是使用类型限制,以及局部元素声明为您提供的有限数量的上下文感知,将属性绑定到相关不同上下文中的不同类型。要做到这一点:

  1. 定义接受所有值集的并集的属性的类型。 (如果属性的三值和二值版本接受不相交的值集合,则将有五个可能的值;如果二值集合仅从集合中删除一种可能性,则它将只有三个。)I&#39;我打电话给这个TA1。

  2. 为属性定义另外两种类型,为特定位置指定所需的三个和两个值的集合,并通过限制从TA1派生它们。我将这些称为TA2和TA3。

  3. 现在定义复杂类型的基本形式,其中所讨论的属性具有类型TA1。定义复杂类型的其他两种形式,用于要限制属性值的两个不同(集合)上下文。我将这些称为CT1,CT2,CT3。

  4. 根据上下文,将相应的元素绑定到CT2或CT3,以获得所需的行为。根据上下文的区分方式,您可能还需要其他元素的不同类型。在a paper I wrote long ago for the XML Schema working group at W3C中详细说明了其工作原理。

  5. 您没有足够详细地描述您的具体案例,以便提供示例解决方案。但想象一下,在frontmatterbackmatter元素中,我们希望para元素接受值&#39; hot&#39;而且温和的&#39;在chili属性中,但在body元素中,唯一可接受的值为redgreenchristmas

    首先,我们定义上述三种类型。

    <xs:simpleType name="TA1">
      <xs:restriction base="xs:string">
        <xs:enumeration value="hot"/>
        <xs:enumeration value="mild"/>
        <xs:enumeration value="red"/>
        <xs:enumeration value="green"/>
        <xs:enumeration value="christmas"/>
      </xs:restriction>
    </xs:simpleType>
    
    <xs:simpleType name="TA2">
      <xs:restriction base="tns:TA1">
        <xs:enumeration value="hot"/>
        <xs:enumeration value="mild"/>
      </xs:restriction>
    </xs:simpleType>
    
    <xs:simpleType name="TA3">
      <xs:restriction base="tns:TA1">
        <xs:enumeration value="red"/>
        <xs:enumeration value="green"/>
        <xs:enumeration value="christmas"/>
      </xs:restriction>
    </xs:simpleType> 
    

    这些之间的派生关系很重要,因为在定义封闭复杂类型的限制时,将TA2和TA3替换为TA1是合法的:

    <xs:complexType name="CT1" mixed="true">
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element ref="tns:emph"/>
        <xs:element ref="tns:q"/>
      </xs:choice>
      <xs:attribute name="chile" type="tns:TA1"/>
    </xs:complexType>
    
    <xs:complexType name="CT2">
      <xs:complexContent mixed="true">
        <xs:restriction base="tns:CT1">
          <xs:choice minOccurs="0" maxOccurs="unbounded">
            <xs:element ref="tns:emph"/>
            <xs:element ref="tns:q"/>
          </xs:choice>
          <xs:attribute name="chile" type="tns:TA2"/>        
        </xs:restriction>
      </xs:complexContent>
    </xs:complexType>
    
    <xs:complexType name="CT3">
      <xs:complexContent mixed="true">
        <xs:restriction base="tns:CT1">
          <xs:choice minOccurs="0" maxOccurs="unbounded">
            <xs:element ref="tns:emph"/>
            <xs:element ref="tns:q"/>
          </xs:choice>
          <xs:attribute name="chile" type="tns:TA3"/>        
        </xs:restriction>
      </xs:complexContent>
    </xs:complexType>
    

    这允许我们定义para元素的两个版本,具体取决于上下文:一个使用tns:TA2键入chili属性,一个使用tns:TA3键入。在这种情况下的复杂性是,段落可以(我们假设)作为各种不同元素的子项出现,也必须以两种不同的方式键入以反映它们所处的上下文。我们对文档,前端,正文,反向的声明,div看起来像这样:

    <xs:element name="document" type="tns:document"/>
    
    <xs:complexType name="document">
      <xs:sequence>
        <xs:element name="frontmatter" type="tns:front-back-section"/>
        <xs:element name="body" type="tns:section"/>
        <xs:element name="backmatter" type="tns:front-back-section"/>
      </xs:sequence>
    </xs:complexType>
    
    <xs:complexType name="front-back-section">
      <xs:sequence>
        <xs:element name="para" type="tns:CT2" 
                    minOccurs="0" maxOccurs="unbounded"/>
        <xs:element name="div" type="tns:front-back-section" 
                    minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
    
    <xs:complexType name="section">
      <xs:sequence>
        <xs:element name="para" type="tns:CT3" 
                    minOccurs="0" maxOccurs="unbounded"/>
        <xs:element name="div" type="tns:section" 
                    minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
    

    在这个词汇表的简单版本中,我们将为div提供一种类型,为para提供一种类型;这里发生的事情基本上是在这个更复杂的版本中,我们将每个类型分成两个,并将div和para元素现在绑定到一个,现在绑定到另一个。通过将div绑定到tns:section或tns:front-back section,我们使div元素从其父节点向其子节点传送一条信息,即&#34;我们是在主体内部,还是在前面内部还是重要的?&#34;该信息允许para元素正确地绑定到tns:CT2或tns:CT3。相同的技术可用于编写无上下文语法,以携带有关周围环境的有限信息。 (一位信息需要将所有需要携带该位的状态加倍。)当然,该技术也可用于Relax NG模式。它不能在DTD中使用,因为它们不允许将给定的元素类型名称绑定到多个类型(或者等效地,它们不允许在多个元素声明中使用给定的元素类型名称)。 / p>