如何在XML Schema中实现多态以兼容两种polly XML结构
需求说明
需要用同一个XML Schema定义两种<polly>元素的结构:
- 直接包含文本内容的结构:
<polly> wibble </polly>
- 包含多个子
<element>元素的结构:
<polly> <element> foo </element> <element> bar </element> <element> baz </element> </polly>
注:示例中元素为string类型,需支持其他类型扩展
尝试的实现方式
我尝试通过基类层级来实现:
- 定义基类型:
<xsd:complexType name="polly"/>
- 定义字符串子类型:
<xsd:complexType name="polly_string"> <xsd:complexContent> <xsd:extension base="tns:polly"/> </xsd:complexContent> </xsd:complexType>
- 定义数组子类型:
<xsd:complexType name="polly_array"> <xsd:complexContent> <xsd:extension base="tns:polly"> <xsd:sequence> <xsd:element maxOccurs="unbounded" minOccurs="0" name="element" type="tns:polly_array_element"/> </xsd:sequence> </xsd:extension> </xsd:complexContent> </xsd:complexType> <xsd:simpleType name="polly_array_element"> <xsd:restriction base="xsd:string"/> </xsd:simpleType>
- 实例化元素:
<xsd:element minOccurs="0" name="polly" type="tns:polly"/>
遇到的问题
目前的定义无法让polly_string成为简单类型,也无法为其添加restriction——因为extension和restriction互斥。我用VS Code验证时遇到报错,尝试了几种写法都失败:
尝试1:在extension内添加restriction
<xsd:complexType name="polly_string"> <xsd:complexContent> <xsd:extension base="tns:polly"> <xsd:restriction base="xs:string"/> </xsd:extension> </xsd:complexContent> </xsd:complexType>
报错:
The content of 'polly_string' is invalid. Element 'restriction' is invalid, misplaced, or occurs too often.
尝试2:使用simpleContent扩展基类型
<xsd:complexType name="polly_string"> <xsd:simpleContent> <xsd:extension base="tns:polly"> </xsd:extension> </xsd:simpleContent> </xsd:complexType>
报错:
Complex Type Definition Representation Error for type 'polly_string'. When is used, the base type must be a complexType whose content type is simple, or, only if restriction is specified, a complex type with mixed content and emptiable particle, or, only if extension is specified, a simple type. 'polly' satisfies none of these conditions.
尝试3:多重extension
<xsd:complexType name="polly_string"> <xsd:complexContent> <xsd:extension base="tns:polly"/> <xsd:extension base="xs:string"/> </xsd:complexContent> </xsd:complexType>
报错:
The content of 'polly_string' is invalid. Element 'extension' is invalid, misplaced, or occurs too often.
解决方案
要实现需求,需要调整基类型的定义,让polly_string能够包含简单文本内容,同时继承基类型。以下是两种可行方案:
方案1:使用xsd:choice定义多态结构
直接在基类型中定义可选的内容模型,让<polly>可以选择是文本内容还是子元素序列,无需子类:
<xsd:complexType name="polly"> <xsd:choice> <!-- 文本内容 --> <xsd:element name="text" type="xsd:string" minOccurs="0"/> <!-- 子元素序列 --> <xsd:sequence minOccurs="0"> <xsd:element maxOccurs="unbounded" name="element" type="tns:polly_array_element"/> </xsd:sequence> </xsd:choice> </xsd:complexType> <xsd:simpleType name="polly_array_element"> <xsd:restriction base="xsd:string"/> </xsd:simpleType> <xsd:element name="polly" type="tns:polly"/>
方案2:调整基类型为可扩展的简单内容类型
如果需要保持子类层级,把基类型定义为支持简单内容的complexType,这样polly_string可以通过simpleContent扩展并添加restriction:
<!-- 基类型定义为带有简单内容的complexType --> <xsd:complexType name="polly"> <xsd:simpleContent> <xsd:extension base="xsd:anyType"/> </xsd:simpleContent> </xsd:complexType> <!-- 字符串子类型:通过simpleContent扩展基类型,并添加restriction --> <xsd:complexType name="polly_string"> <xsd:simpleContent> <xsd:restriction base="tns:polly"> <xsd:simpleType> <xsd:restriction base="xsd:string"> <!-- 可在此添加自定义restriction规则,如长度、正则等 --> </xsd:restriction> </xsd:simpleType> </xsd:restriction> </xsd:simpleContent> </xsd:complexType> <!-- 数组子类型:通过complexContent扩展基类型,添加子元素 --> <xsd:complexType name="polly_array"> <xsd:complexContent> <xsd:extension base="tns:polly"> <xsd:sequence> <xsd:element maxOccurs="unbounded" name="element" type="tns:polly_array_element"/> </xsd:sequence> </xsd:extension> </xsd:complexContent> </xsd:complexType> <xsd:simpleType name="polly_array_element"> <xsd:restriction base="xsd:string"/> </xsd:simpleType> <!-- 使用基类型实例化,XML中通过xsi:type指定具体子类型 --> <xsd:element name="polly" type="tns:polly"/>
使用该方案时,XML实例需通过xsi:type指定具体子类型:
- 字符串结构:
<polly xsi:type="tns:polly_string">wibble</polly>
- 数组结构:
<polly xsi:type="tns:polly_array"> <element>foo</element> <element>bar</element> </polly>
内容的提问来源于stack exchange,提问作者andreasmartens

