问题
I want to validate polymorphic Shape
elements, differentiated by the Type
child element (not attribute) value. Below are sibling Circle and Rectangle Shape
elements. Circles have a Radius
and only 1 Point
. Rectangles don't have Radius
and have 4 Point
elements:
<?xml version="1.0" encoding="UTF-8" ?>
<Shapes>
<Shape>
<Type>Circle</Type>
<ID>A1234</ID>
<Label>This is round</Label>
<Radius>5.4</Radius>
<Points>
<Point>
<X>5.00</X>
<Y>2.00</Y>
</Point>
</Points>
</Shape>
<Shape>
<Type>Rectangle</Type>
<ID>B4567</ID>
<Label>This is not round</Label>
<Points>
<Point>
<X>0.00</X>
<Y>0.00</Y>
</Point>
<Point>
<X>4.00</X>
<Y>0.00</Y>
</Point>
<Point>
<X>4.00</X>
<Y>2.00</Y>
</Point>
<Point>
<X>0.00</X>
<Y>2.00</Y>
</Point>
</Points>
</Shape>
</Shapes>
Here's a NON-functional schema along the lines of what I was hoping to do:
<xsd:simpleType name="ShapeTypeEnum">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Circle"/>
<xsd:enumeration value="Rectangle"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="ShapeBase">
<xsd:sequence>
<xsd:element name="Type" type="ShapeTypeEnum"/>
<xsd:element name="ID" type="xsd:string"/>
<xsd:element name="Label" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Shape" type="Circle">
<xsd:complexContent>
<xsd:extension base="ShapeBase">
<xsd:all>
<xsd:element name="Radius" type="xsd:decimal"/>
<xsd:element name="Points">
<xsd:complexType>
<xsd:sequence>
<xsd:element minOccurs="1" maxOccurs="1" name="Point" type="Point"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:all>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Shape" type="Rectangle">
<xsd:complexContent>
<xsd:extension base="ShapeBase">
<xsd:all>
<xsd:element name="Points">
<xsd:complexType>
<xsd:sequence>
<xsd:element minOccurs="4" maxOccurs="4" name="Point" type="Point"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:all>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Point">
<xsd:all>
<xsd:element name="X" type="xsd:decimal"/>
<xsd:element name="Y" type="xsd:decimal"/>
</xsd:all>
</xsd:complexType>
The lines <xsd:complexType name="Shape" type="Rectangle">
and <xsd:complexType name="Shape" type="Circle">
don't work. Is it possible to validate identically named elements with different schema sections based on the value of a child Element (Type
)?
回答1:
In XSD 1.0, it can't be done. In XSD 1.1, it can, using assertions.
Though even using assertions, it's not that easy (it would be much easier if Type
were an attribute). You need to define a content model that's effectively a union of all the different models for different shapes (you can't use a simple xs:choice
in this example because it would violate UPA), and then you need to define assertions like
<xs:assert test="exists(radius) = (type = 'Circle')"/>
<xs:assert test="count(points) = 4 or type != 'Rectangle'"/>
XSD 1.1 is supported in Altova, Saxon, and Xerces, but not for example by the Microsoft schema processor.
回答2:
Michael Kay has explained how you might use xs:assert
to achieve your requested validations, assuming that your XML design cannot be changed. However, it's worth pointing out that your XML design itself is essentially problematic:
Generic element name plus type is an anti-pattern in XML
Bad
<Thing>
<Type>TrueNature</Type>
<!-- ... -->
</Thing>
XSD 1.1 and assertions are required to constrain based on TrueNature
.
See also:
- Vary type in XSD according to element value?
- XML Restrict based on another element value
- Require XML element in XSD when another element has certain value?
Better
<Thing type="TrueNature">
<!-- ... -->
</Thing>
XSD 1.1 and assertions or Conditional Type Assignment are required to constrain based on TrueNature
.
Best
<TrueNature>
<!-- ... -->
</TrueNature>
XSD 1.0 suffixes because the element name itself reflects its true nature.
来源:https://stackoverflow.com/questions/57684809/can-i-validate-polymorphic-xml-elements-based-on-a-child-type-element-value