我可以根据子 Type 元素值验证多态 XML 元素吗?

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

我想验证多态 Shape 元素,由 Type 子元素(not 属性)值区分。下面是兄弟 Circle 和 Rectangle Shape 元素。圈子有一个Radius,只有1个Point。矩形没有 Radius 而有 4 个 Point 元素:

<?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>

这是一个非功能性架构,与我希望做的一致:

  <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>

<xsd:complexType name="Shape" type="Rectangle"><xsd:complexType name="Shape" type="Circle"> 不起作用。是否可以根据子元素 (Type) 的值验证具有不同模式部分的同名元素?

在XSD 1.0 中,无法完成。在 XSD 1.1 中,它可以使用断言。

虽然即使使用断言,也不是那么容易(如果 Type 是一个属性会容易得多)。您需要定义一个内容模型,该模型实际上是针对不同形状的所有不同模型的联合(您不能在此示例中使用简单的 xs:choice,因为它会违反 UPA),然后您需要定义断言,例如

<xs:assert test="exists(radius) = (type = 'Circle')"/>

<xs:assert test="count(points) = 4 or type != 'Rectangle'"/>

XSD 1.1 在 Altova、Saxon 和 Xerces 中受支持,但例如 Microsoft 模式处理器不支持。

Michael Kay 已经解释了如何使用 xs:assert 来实现您请求的验证,假设您的 XML 设计无法更改。但是,值得指出的是,您的 XML 设计本身存在本质问题:

通用元素名称加类型是 XML

中的反模式

不好

<Thing>
  <Type>TrueNature</Type>
  <!-- ... -->
</Thing>

XSD 1.1和断言需要基于TrueNature.

进行约束

另请参阅:

  • Vary type in XSD according to element value?

更好

<Thing type="TrueNature">
  <!-- ... -->
</Thing>

XSD 1.1 and assertions or 需要根据TrueNature.

进行约束

最佳

<TrueNature>
  <!-- ... -->
</TrueNature>

XSD 1.0 后缀,因为元素名称本身就反映了它的真实性质。