XML 1.0下如何基于元素属性值创建差异化验证Schema?
解决XML元素基于属性值的差异化验证问题
这个问题确实戳中了XML Schema 1.0的一个核心局限——它原生不支持基于属性值的条件验证逻辑。不过别担心,我们有几种可行的解决办法,分情况来看:
方案一:升级到XML Schema 1.1(最优解)
XML Schema 1.1引入了条件类型赋值(Conditional Type Assignment, CTA),完美匹配你要的场景:根据type属性的值,给elem元素分配不同的内容模型。
下面是完整的XSD示例:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" vc:minVersion="1.1"> <!-- 定义type="A"对应的elem内容模型 --> <xs:complexType name="ElemAType"> <xs:sequence> <xs:element name="foo"/> </xs:sequence> <xs:attribute name="type" fixed="A"/> </xs:complexType> <!-- 定义type="B"对应的elem内容模型 --> <xs:complexType name="ElemBType"> <xs:sequence> <xs:element name="goo"/> </xs:sequence> <xs:attribute name="type" fixed="B"/> </xs:complexType> <!-- 根元素定义,使用条件类型赋值 --> <xs:element name="elements"> <xs:complexType> <xs:sequence> <xs:element name="elem" maxOccurs="unbounded"> <xs:alternative test="@type = 'A'" type="ElemAType"/> <xs:alternative test="@type = 'B'" type="ElemBType"/> <!-- 可选:添加默认类型或错误提示 --> <xs:alternative type="xs:error" description="Invalid type value for elem"/> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
这个XSD会自动完成以下验证:
- 所有
type="A"的elem必须包含且仅包含<foo/>子元素,且type属性值固定为"A" - 所有
type="B"的elem必须包含且仅包含<goo/>子元素,且type属性值固定为"B" - 如果
type是其他值,会直接抛出验证错误
方案二:坚守XML Schema 1.0的变通方案
如果因为环境限制必须使用XSD 1.0,只能采用一些变通方法——毕竟1.0本身不支持属性驱动的内容模型切换:
方法1:使用抽象元素+替换组
把elem定义为抽象元素,再创建elemA和elemB两个具体元素分别对应不同类型,同时强制它们的type属性值:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="elem" abstract="true"/> <xs:complexType name="ElemAType"> <xs:sequence> <xs:element name="foo"/> </xs:sequence> <xs:attribute name="type" fixed="A"/> </xs:complexType> <xs:element name="elemA" type="ElemAType" substitutionGroup="elem"/> <xs:complexType name="ElemBType"> <xs:sequence> <xs:element name="goo"/> </xs:sequence> <xs:attribute name="type" fixed="B"/> </xs:complexType> <xs:element name="elemB" type="ElemBType" substitutionGroup="elem"/> <xs:element name="elements"> <xs:complexType> <xs:sequence> <xs:element ref="elem" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
对应的XML需要写成:
<elements> <elemA type="A"> <foo/> </elemA> <elemB type="B"> <goo/> </elemB> </elements>
缺点是XML里的元素名从elem变成了elemA/elemB,但能保证验证逻辑的正确性。
方法2:结合Schematron补充验证
如果必须保持XML里的elem元素名不变,可以用XSD 1.0定义基础结构,再用Schematron规则添加条件验证:
- 先用XSD定义
elem可以包含foo或goo,同时type属性只能是"A"或"B" - 再写Schematron规则,强制
type="A"时必须有foo、不能有goo;type="B"时必须有goo、不能有foo
示例Schematron规则:
<?xml version="1.0" encoding="UTF-8"?> <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> <sch:pattern name="Elem type validation"> <sch:rule context="elem[@type='A']"> <sch:assert test="foo">type="A"的elem必须包含foo子元素</sch:assert> <sch:assert test="count(*)=1 and not(goo)">type="A"的elem只能包含foo子元素</sch:assert> </sch:rule> <sch:rule context="elem[@type='B']"> <sch:assert test="goo">type="B"的elem必须包含goo子元素</sch:assert> <sch:assert test="count(*)=1 and not(foo)">type="B"的elem只能包含goo子元素</sch:assert> </sch:rule> </sch:pattern> </sch:schema>
这种方法需要支持Schematron的验证工具,但能完美保留你原始的XML结构。
内容的提问来源于stack exchange,提问作者Maroš Beťko
相关产品推荐
相关产品推荐

