【问题标题】:Restict XML elements based on the type they extend根据扩展的类型限制 XML 元素
【发布时间】:2018-03-01 05:04:50
【问题描述】:

我有以下 xsd 架构:

<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:complexType name="Vehicle">
  <xs:sequence>
    <xs:element name="numberOfWheels" type="xs:int" minOccurs="0" maxOccurs="1"/>
  </xs:sequence>
</xs:complexType>

<xs:element name="car" type="Car"/>
<xs:complexType name="Car">
  <xs:complexContent>
    <xs:extension base="Vehicle">
      <xs:sequence>
        <xs:element name="numberOfSeats" type="xs:int" minOccurs="0" maxOccurs="1"/>
      </xs:sequence>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

<xs:element name="motorCycle" type="MotorCycle"/>
<xs:complexType name="MotorCycle">
  <xs:complexContent>
    <xs:extension base="Vehicle">
      <xs:sequence>
        <xs:element name="hasLuggageCompartment" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
      </xs:sequence>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

<xs:element name="wagon" type="Wagon"/>
<xs:complexType name="Wagon">
  <xs:complexContent>
    <xs:extension base="Vehicle">
      <xs:sequence>
        <xs:element name="typeOfCargo" type="xs:string" minOccurs="0" maxOccurs="1"/>
      </xs:sequence>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

<xs:element name="vehicleList" type="VehicleList"/>
<xs:complexType name="VehicleList">
  <xs:sequence>
    <xs:element <!-- what to do here? --> minOccurs="1" maxOccurs="unbounded"/>
  </xs:sequence>
</xs:complexType>

</xs:schema>

如架构所示,我们有类型“Car”、“MotorCycle”和“Wagon”,它们都扩展了“Vehicle”类型。

我现在想在我的模式 (VehicleList) 中引入一个容器元素,它可以存储任何类型的元素,只要它扩展“Vehicle”,这样我们就可以存储“Car”、“MororCycle”或“Wagon”,但不是,例如,“Cow”类型的元素,因为它们会扩展“Animal”类型。

我已经搜索了很多,我认为不可能像我们在 OO 编程中经常使用的那样基于“祖先”类型在 XML 模式中引入这种限制。

所以我的问题:是否可以声明一个容器元素,根据它们扩展的类型来限制它包含的元素,如果可以,如何?

提前感谢您的回答和建议。

【问题讨论】:

  • 您能否与我们分享一个 XML 文档,该文档应通过您想要的 XSD 进行验证?

标签: xml xsd containers restriction ancestor


【解决方案1】:

使用 ComplexTypes 和 xsi:type

最简单和最可扩展的方法就是将类型设置为 Vehicle。

<xs:complexType name="VehicleList">
    <xs:sequence>
        <xs:element name="VehicleElm"
                       type="Vehicle"
                       minOccurs="1"
                       maxOccurs="unbounded" />
    </xs:sequence>
</xs:complexType>

但是生成的 XML 看起来像这样,这可能并不完全是您想要的(添加 xsi:type 属性)

<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Liquid Studio 2018 - Developer Bundle 16.0.0.0 (https://www.liquid-technologies.com) -->
<vehicleList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                 xsi:noNamespaceSchemaLocation="XSDFile.xsd">
    <VehicleElm xsi:type="Car">
        <numberOfWheels>4</numberOfWheels>
        <numberOfSeats>2</numberOfSeats>
    </VehicleElm>
    <VehicleElm xsi:type="Wagon">
        <numberOfWheels>6</numberOfWheels>
        <typeOfCargo>stuff</typeOfCargo>
    </VehicleElm>
</vehicleList>

使用替换组

替代方法是使用替换组,这在 XSD 中稍微复杂一些,但提供更清晰的 XML

<?xml version="1.0" encoding="utf-8" ?>
<!--Created with Liquid Studio 2018 - Developer Bundle 16.0.0.0 (https://www.liquid-technologies.com)-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:complexType name="VehicleType">
        <xs:sequence>
            <xs:element name="numberOfWheels"
                           type="xs:int"
                           minOccurs="0"
                           maxOccurs="1" />
        </xs:sequence>
    </xs:complexType>
    <xs:element name="Vehicle"
                   type="VehicleType"
                   abstract="true" />
    <xs:element name="car"
                   substitutionGroup="Vehicle">
        <xs:complexType>
            <xs:complexContent>
                <xs:extension base="VehicleType">
                    <xs:sequence>
                        <xs:element name="numberOfSeats"
                                       type="xs:int"
                                       minOccurs="0"
                                       maxOccurs="1" />
                    </xs:sequence>
                </xs:extension>
            </xs:complexContent>
        </xs:complexType>
    </xs:element>
    <xs:element name="motorCycle"
                   substitutionGroup="Vehicle">
        <xs:complexType>
            <xs:complexContent>
                <xs:extension base="VehicleType">
                    <xs:sequence>
                        <xs:element name="hasLuggageCompartment"
                                       type="xs:boolean"
                                       minOccurs="0"
                                       maxOccurs="1" />
                    </xs:sequence>
                </xs:extension>
            </xs:complexContent>
        </xs:complexType>
    </xs:element>
    <xs:element name="wagon"
                   substitutionGroup="Vehicle">
        <xs:complexType>
            <xs:complexContent>
                <xs:extension base="VehicleType">
                    <xs:sequence>
                        <xs:element name="typeOfCargo"
                                       type="xs:string"
                                       minOccurs="0"
                                       maxOccurs="1" />
                    </xs:sequence>
                </xs:extension>
            </xs:complexContent>
        </xs:complexType>
    </xs:element>
    <xs:element name="vehicleList"
                   type="VehicleList" />
    <xs:complexType name="VehicleList">
        <xs:sequence>
            <xs:element ref="Vehicle"
                           minOccurs="1"
                           maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>
</xs:schema>

生成的 XML 然后看起来像这样

<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Liquid Studio 2018 - Developer Bundle 16.0.0.0 (https://www.liquid-technologies.com) -->
<vehicleList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                 xsi:noNamespaceSchemaLocation="XSDFile2.xsd">
    <car>
        <numberOfWheels>4</numberOfWheels>
        <numberOfSeats>2</numberOfSeats>
    </car>
    <motorCycle>
        <numberOfWheels>2</numberOfWheels>
        <hasLuggageCompartment>false</hasLuggageCompartment>
   </motorCycle>
</vehicleList>

基本上每个你可以使用Vehicle元素的地方,任何替换组成员都可以在它的位置使用,并且由于Vehicle元素是抽象的,它不能被自己使用(所以你必须使用替代组成员)。

【讨论】:

  • 非常感谢!确实,与替换组成员一起工作,我现在第一次看到,在 xsd 级别上有点复杂,但它显然会导致更清晰的 xml。我想我会这样做,因为这正是我想要的行为。
  • @WimHuygen,如果回复回答了您的问题,请接受。否则看起来你的问题还没有解决。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-22
  • 2016-10-15
  • 1970-01-01
相关资源
最近更新 更多