Can I validate polymorphic XML elements based on a child Type element value?

跟風遠走 提交于 2021-02-10 18:48:14

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!