我在xsd中使用复杂的公共类型,并且我想根据元素的位置(xpath)更改其中一个属性的可能值。
我目前正在使用枚举集来定义可能的值,但是如果元素出现在xml文档中的一个特定位置,我想为此属性提供3个可能的值,如果它出现在其他任何位置,我希望有2个可能的值。
是否可以使用xsd强制执行此约束?我知道我可以定义2个单独的元素,但是有问题的元素包含许多子元素和属性,我只需要对一个特定属性进行更改。
我们使用的验证引擎只是Schema 1.0 complient。
答案 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>
该元素可能出现在root
或child
元素中:
<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)。
如果对于您来说重要的是所讨论的元素在类型系统中明确相关,事情会变得更复杂,但这种技术非常值得学习。
一种方法是使用类型限制,以及局部元素声明为您提供的有限数量的上下文感知,将属性绑定到相关不同上下文中的不同类型。要做到这一点:
定义接受所有值集的并集的属性的类型。 (如果属性的三值和二值版本接受不相交的值集合,则将有五个可能的值;如果二值集合仅从集合中删除一种可能性,则它将只有三个。)I&#39;我打电话给这个TA1。
为属性定义另外两种类型,为特定位置指定所需的三个和两个值的集合,并通过限制从TA1派生它们。我将这些称为TA2和TA3。
现在定义复杂类型的基本形式,其中所讨论的属性具有类型TA1。定义复杂类型的其他两种形式,用于要限制属性值的两个不同(集合)上下文。我将这些称为CT1,CT2,CT3。
根据上下文,将相应的元素绑定到CT2或CT3,以获得所需的行为。根据上下文的区分方式,您可能还需要其他元素的不同类型。在a paper I wrote long ago for the XML Schema working group at W3C中详细说明了其工作原理。
您没有足够详细地描述您的具体案例,以便提供示例解决方案。但想象一下,在frontmatter
和backmatter
元素中,我们希望para
元素接受值&#39; hot&#39;而且温和的&#39;在chili
属性中,但在body
元素中,唯一可接受的值为red
,green
和christmas
。
首先,我们定义上述三种类型。
<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>