如何根据同元素中兄弟属性值设置XSD属性类型
根据属性值动态约束另一属性的XSD类型
这个需求确实挺常见的——根据同一元素里另一个属性的值,来约束当前属性的类型对吧?不过得先说明:XSD 1.0本身不支持这种条件式的类型关联,所以得用些变通方案;而XSD 1.1提供了原生支持,实现起来会更顺畅。下面分别给你两种场景的具体实现方式:
一、XSD 1.0解决方案(适配旧版解析器)
如果你的项目还在使用只支持XSD 1.0的旧版解析器,咱们得用「联合类型+额外验证」的组合方案:
1. 定义覆盖所有格式的联合类型
先创建一个包含所有允许格式的联合类型,确保ProductCode至少符合其中一种格式要求:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Products"> <xs:complexType> <xs:sequence> <xs:element name="Product" maxOccurs="unbounded"> <xs:complexType> <xs:attribute name="ProductCodeType" use="required"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="Default"/> <xs:enumeration value="Manufacturer"/> <xs:enumeration value="Supplier"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="ProductCode" use="required"> <xs:simpleType> <xs:union memberTypes="DefaultCodeType ManufacturerCodeType SupplierCodeType"/> </xs:simpleType> </xs:attribute> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> <!-- 定义每种类型的具体格式约束 --> <xs:simpleType name="DefaultCodeType"> <xs:restriction base="xs:integer"> <!-- 这里假设Default类型是3-7位数字,你可以根据实际需求调整 --> <xs:minInclusive value="100"/> <xs:maxInclusive value="9999999"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="ManufacturerCodeType"> <xs:restriction base="xs:string"> <!-- 假设Manufacturer类型是13位数字(比如EAN-13编码) --> <xs:pattern value="[0-9]{13}"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="SupplierCodeType"> <xs:restriction base="xs:string"> <!-- 假设Supplier类型是字母开头,后跟任意数字 --> <xs:pattern value="[A-Za-z][0-9]+"/> </xs:restriction> </xs:simpleType> </xsd>
2. 补充条件校验(用Schematron)
上面的XSD只能保证ProductCode格式合法,但没法确保它和ProductCodeType对应。这时候可以用Schematron来添加条件规则,补上这个关联约束:
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> <sch:pattern name="ProductCode必须匹配对应的ProductCodeType"> <sch:rule context="Product[@ProductCodeType='Default']"> <sch:assert test="matches(@ProductCode, '^[0-9]{3,7}$')"> Default类型的ProductCode必须是3-7位数字哦! </sch:assert> </sch:rule> <sch:rule context="Product[@ProductCodeType='Manufacturer']"> <sch:assert test="matches(@ProductCode, '^[0-9]{13}$')"> Manufacturer类型的ProductCode必须是13位数字哦! </sch:assert> </sch:rule> <sch:rule context="Product[@ProductCodeType='Supplier']"> <sch:assert test="matches(@ProductCode, '^[A-Za-z][0-9]+$')"> Supplier类型的ProductCode必须以字母开头后跟数字哦! </sch:assert> </sch:rule> </sch:pattern> </sch:schema>
二、XSD 1.1解决方案(原生支持条件约束)
如果你的解析器支持XSD 1.1(比如Saxon、Xerces2等),那直接用原生的条件类型分配或者断言就行,不用绕弯子:
方式1:条件类型分配(推荐)
直接根据ProductCodeType的取值,为ProductCode指定对应的类型,逻辑清晰又直观:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" vc:minVersion="1.1"> <xs:element name="Products"> <xs:complexType> <xs:sequence> <xs:element name="Product" maxOccurs="unbounded"> <xs:complexType> <xs:attribute name="ProductCodeType" use="required"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="Default"/> <xs:enumeration value="Manufacturer"/> <xs:enumeration value="Supplier"/> </xs:restriction> </xs:simpleType> </xs:attribute> <!-- 核心:根据ProductCodeType的值动态选择类型 --> <xs:attribute name="ProductCode" use="required"> <xs:simpleType> <xs:alternative test="@ProductCodeType='Default'" type="DefaultCodeType"/> <xs:alternative test="@ProductCodeType='Manufacturer'" type="ManufacturerCodeType"/> <xs:alternative test="@ProductCodeType='Supplier'" type="SupplierCodeType"/> </xs:simpleType> </xs:attribute> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> <!-- 复用之前定义的各类型约束 --> <xs:simpleType name="DefaultCodeType"> <xs:restriction base="xs:integer"> <xs:minInclusive value="100"/> <xs:maxInclusive value="9999999"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="ManufacturerCodeType"> <xs:restriction base="xs:string"> <xs:pattern value="[0-9]{13}"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="SupplierCodeType"> <xs:restriction base="xs:string"> <xs:pattern value="[A-Za-z][0-9]+"/> </xs:restriction> </xs:simpleType> </xsd>
方式2:使用断言(灵活度更高)
如果需要更复杂的校验逻辑,也可以在Product元素的complexType里添加断言,直接校验属性值的关联关系:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" vc:minVersion="1.1"> <xs:element name="Products"> <xs:complexType> <xs:sequence> <xs:element name="Product" maxOccurs="unbounded"> <xs:complexType> <xs:attribute name="ProductCodeType" use="required"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="Default"/> <xs:enumeration value="Manufacturer"/> <xs:enumeration value="Supplier"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="ProductCode" use="required" type="xs:string"/> <!-- 添加断言规则,直接关联两个属性 --> <xs:assert test=" (@ProductCodeType='Default' and matches(@ProductCode, '^[0-9]{3,7}$')) or (@ProductCodeType='Manufacturer' and matches(@ProductCode, '^[0-9]{13}$')) or (@ProductCodeType='Supplier' and matches(@ProductCode, '^[A-Za-z][0-9]+$')) "> ProductCode的格式必须和对应的ProductCodeType匹配哦! </xs:assert> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xsd>
总结一下
- 要是能用XSD 1.1,优先选条件类型分配,逻辑清晰且是原生支持;
- 只能用XSD 1.0的话,就用「联合类型+Schematron」的组合,先保证格式合法,再补上关联约束。
内容的提问来源于stack exchange,提问作者dies_felices
相关产品推荐
相关产品推荐

