XML Schema用于具有相同名称但属性值不同的元素序列?

时间:2009-05-05 21:45:54

标签: xml xsd

如何为实例文档指定XML模式,如下所示:

<productinfo>
  <!-- other stuff -->
  <informationset type="Manufacturer">
    <!-- content not relevant -->
  </informationset>
  <informationset type="Ingredients">
    <!-- content not relevant -->
  </informationset>
</productinfo>

即包含两个“信息集”子项序列的“productinfo”元素,第一个包含@type="Manufacturer",第二个包含@type="Ingredients"

5 个答案:

答案 0 :(得分:3)

注意这个答案是不正确的,正如Serge指出的那样。

使用 xerces进行测试会出现此错误:type.xsd:3:21: cos-element-consistent: Error for type '#AnonType_productinfo'. Multiple elements with name 'informationset', with different types, appear in the model group. cos-element-consistent的规范中有更多详细信息。

但是有一个解决方案,类似于下面Marc的答案,但仍然使用类型。如果它们位于超类型的minOccurs / maxOccurs列表中,则可以多次出现相同的不同类型,这些列表由其他类型扩展。也就是说,就像java或C#中的多态类列表一样。这克服了上面的问题,因为尽管该元素名称可以在xml中出现多次,但它只在xsd中出现一次。

以下是xsd和xml示例 - 这次使用 xerces 进行测试!:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="productinfo">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="informationset" type="supertype" minOccurs="2" maxOccurs="2"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:complexType name="supertype">
  </xs:complexType>

  <xs:complexType name="Manufacturer">
    <xs:complexContent>
      <xs:extension base="supertype">
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

  <xs:complexType name="Ingredients">
    <xs:complexContent>
      <xs:extension base="supertype">
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

</xs:schema>

<productinfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <informationset xsi:type="Manufacturer"></informationset>
  <informationset xsi:type="Ingredients"></informationset>
</productinfo>

注意:您无法控制不同类型的订单,或每种类型出现的次数(每种类型可能出现一次,多次或根本不出现) - 就像java或C#中的多态类列表。但是你至少可以指定整个列表的确切长度(如果你愿意)。

例如,我已将上述示例限制为两个元素,但未设置顺序(即制造商可以是第一个,或者成分可以是第一个);并且没有设置重复次数(即它们可以是制造商,或者两者都是成分,或者两者之一)。


您可以使用XML Schema type ,如:

<productinfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <informationset xsi:type="Manufacturer"></informationset>
  <informationset xsi:type="Ingredients"></informationset>
</productinfo>

XSD为每一个定义了单独的复杂类型:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="productinfo">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="informationset" type="Manufacturer"/>
        <xs:element name="informationset" type="Ingredients"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:complexType name="Manufacturer">
  </xs:complexType>
  <xs:complexType name="Ingredients">
  </xs:complexType>
</xs:schema>

这是xsi:type的一个特例。通常,不要认为您可以指定属性在同一个元素的元素中具有不同的值,因为它们是同一元素的不同定义。

我不是100%明确原因 - 任何人都知道规范的相关部分吗?

答案 1 :(得分:2)

您可以尝试这样的方法 - 为“informationSet”元素创建单独的complexType,并将属性限制为有效字符串列表:

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" 
           xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="productinfo">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" 
                    name="informationset" type="informationSetType" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:complexType name="informationSetType">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="type" type="validAttributeType" use="required" />
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

  <xs:simpleType name="validAttributeType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="Manufacturer" />
      <xs:enumeration value="Ingredients" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

当然,如果您愿意,可以扩展有效属性名称列表 - 只需在限制枚举列表中添加更多元素。

马克

答案 2 :(得分:1)

使用断言架构组件

感谢 XML Schema 1.1 ,有一种方法可以强制执行您的需求,而无需将xsi命名空间和hack多态性导入XML文档。 XML Schema 1.1带来了两个新组件assertionstype alternatives,提供了xs:assertxs:alternative元素。它们具有@test属性,其中条件和约束被指定为 XPath 2.0表达式

即使xs:alternative显然是你问题的解决方案,但就我而言,我还没有成功地根据元素的位置分配替代类型。 (模式解析器对节点上下文的理解似乎与我根据经验证的XML文档结构所预期的不同。

无论如何,您可以使用xs:assert强制执行约束:

<xs:element name="productinfo">
  <xs:complexType>
    <xs:sequence minOccurs="2" maxOccurs="unbounded">
      <xs:element name="informationset">
        <xs:complexType>
          <xs:attribute name="type" use="required">
            <xs:simpleType>
              <xs:restriction base="xs:string">
                <xs:enumeration value="Manufacturer" />
                <xs:enumeration value="Ingredients" />
              </xs:restriction>
            </xs:simpleType>
          </xs:attribute>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:assert test="informationset[1]/@type='Manufacturer'" xpathDefaultNamespace="##targetNamespace"/>
    <xs:assert test="informationset[2]/@type='Ingredients'" xpathDefaultNamespace="##targetNamespace"/>
  </xs:complexType>
</xs:element>

如果架构的命名空间为空,则可能不需要使用属性@xpathDefaultNamespace,但如果架构定义了一个,则必须设置为##targetNamespace,否则评估元素名称将失败。

  

注意:显然,要使其工作,必须使用支持XML Schema 1.1的验证器。使用OxygenXML时,您只需在 XML / XML Parser / XML Schema 首选项页面中将默认XML架构版本设置为1.1。使用Xerces作为验证器时,必须激活additional featurehttp://apache.org/xml/features/validation/cta-full-xpath-checking。否则,它将无法评估您提出的大部分XPath,因为默认情况下它使用的子集不足。

答案 3 :(得分:0)

答案 4 :(得分:0)

在VS中,报告错误:具有相同名称且在相同范围内的元素必须具有相同的类型。
我认为XSD无法满足您的要求,您可以尝试从另一个方向解决它。例如,使用xslt来验证XML。 XSLT是基于xpath和规则的,它可以检查xml中的每个位置 XSD + XSLT是一个很好的解决方案,XSD用于模式检查,XSLT用于信息检查。