如何在XSD 1.0中定义具有两种形态的box元素?
XSD 1.0 实现box元素双形态验证的解决方案
问题概述
需要编写XSD 1.0验证XML文档,其中<box>元素存在两种互斥形态:
- 形态1:无内容,必填属性
name、weight,可选属性height - 形态2:包含若干子元素(可嵌套
<box>),仅必填属性name,无其他属性
示例XML
<box name="box_1"> <!-- type 2 --> <box name="box_2"> <!-- type 2 --> <box name="box_3" weight="10" height="5" /> <!-- type 1 --> <box name="box_4" weight="15" /> <!-- type 1 --> </box> <box name="box_5"> <!-- type 2 --> <box name="box_6" weight="21" /> <!-- type 1 --> </box> </box> <box name="box_7" weight="21" /> <!-- type 1 -->
遇到的问题
- XSD 1.0不支持
<xs:alternative>,无法直接为同一元素定义条件类型 - 无法在
<xs:choice>中直接添加属性约束 - 重复定义同名
<box>元素会导致验证器优先匹配第一个类型,引发错误 weight属性在形态1中必须为必填,不能设为可选- 无法修改目标XML文档
无效的尝试方案
以下Schema因重复定义同名元素导致验证失败:
<xs:complexType name="closet"> <xs:choice maxOccurs="unbounded"> <xs:element name="box" type="TboxComplex" /> <xs:element name="box" type="TboxSimple" /> </xs:choice> </xs:complexType> <xs:complexType name="TboxComplex"> <xs:choice maxOccurs="unbounded"> <xs:element name="box" type="TboxComplex" /> <xs:element name="box" type="TboxSimple" /> </xs:choice> <xs:attribute name="name" type="xs:string" use="required" /> </xs:complexType> <xs:complexType name="TboxSimple"> <xs:attribute name="name" type="xs:string" use="required" /> <xs:attribute name="weight" type="xs:integer" use="required" /> <xs:attribute name="height" type="xs:integer" use="optional" /> </xs:complexType>
验证报错信息
> xmllint --schema room.xsd room.xml room.xml:9: element box: Schemas validity error : Element 'box', attribute 'weight': The attribute 'weight' is not allowed. room.xml:9: element box: Schemas validity error : Element 'box', attribute 'height': The attribute 'height' is not allowed. room.xml:9: element box: Schemas validity error : Element 'box': Missing child element(s). Expected is ( box ).
解决方案
由于XSD 1.0本身不支持基于内容的条件属性约束,我们需要结合XSD基础验证+Schematron规则来实现完整约束,无需修改XML或切换至XSD 1.1。
步骤1:编写基础XSD Schema
该Schema定义<box>元素的所有可能结构,放宽属性的必填性,后续用Schematron补充条件约束:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="root"> <!-- 可根据实际文档调整根元素名称 --> <xs:complexType> <xs:choice maxOccurs="unbounded"> <xs:element ref="box"/> </xs:choice> </xs:complexType> </xs:element> <xs:element name="box"> <xs:complexType> <xs:choice maxOccurs="unbounded" minOccurs="0"> <xs:element ref="box"/> </xs:choice> <xs:attribute name="name" type="xs:string" use="required"/> <xs:attribute name="weight" type="xs:integer"/> <xs:attribute name="height" type="xs:integer"/> </xs:complexType> </xs:element> </xs:schema>
步骤2:编写Schematron规则
添加条件约束,实现两种形态的互斥验证:
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> <sch:pattern name="Box形态约束"> <!-- 约束1:无子元素的box必须包含weight属性 --> <sch:rule context="box[not(*)]"> <sch:assert test="@weight">无子元素的box必须包含必填属性weight</sch:assert> </sch:rule> <!-- 约束2:有子元素的box不能包含weight或height属性 --> <sch:rule context="box[*]"> <sch:assert test="not(@weight)">有子元素的box不能包含weight属性</sch:assert> <sch:assert test="not(@height)">有子元素的box不能包含height属性</sch:assert> </sch:rule> </sch:pattern> </sch:schema>
验证方式
使用支持Schematron的工具(如带Schematron插件的xmllint、Oxygen XML Editor等)进行验证:先通过XSD验证结构合法性,再通过Schematron验证业务规则。
如果必须仅使用XSD 1.0而不借助Schematron,无法完全实现weight的条件必填约束,只能接受放宽属性要求,但可通过注释明确业务规则。
内容的提问来源于stack exchange,提问作者T.P.
相关产品推荐
相关产品推荐

