XML Schema验证:如何验证“嵌入式”文档?

时间:2010-12-28 10:32:19

标签: xml validation xsd

我不确定我的问题的标题是否正确,我不认为这被称为“嵌入式”文档,但我不知道确切的术语。所以,首先:“这个人意味着什么?”

<somedocument>
    <item cost="34">
        <name>Apple</name>
        <description>A delicious apple!</description>
        <details xmlns:apple="http://example.org/apples">
            <apple:color>red</apple:color>
            <apple:cultivar>Braeburn</apple:cultivar>
        </details>
    </item>
</somedocument>

现在我想使用XMLSchema验证此文档。我会为文档的所有通用细节创建一个模式,比如项目结构等。我知道我可以使用<xsd:any>允许其他文档中的任何其他XML,我可以使用它来允许<details>部分中的苹果特定元素。到目前为止,非常好。

但是如果我想验证http://example.org/apples命名空间中的所有元素呢?鉴于该文件不仅限于苹果,可能还有香蕉,猕猴桃和桃子,未来可能会有更多的“嵌入式”文件。

我错过了XMLSchema中的哪些内容可以帮助我做到这一点?如果没有,在这种情况下你会怎么做?您能想到的替代品有哪些?由于我还使用Schemas进行文档管理,“只需解析文档并自行验证代码中的所有<details>”解决方案并不理想。

提前谢谢!

修改的: 在再次阅读完整个问题之后,有一个重点缺失:

  • <details>部分不限于示例中所示的键/值对,因此使用通用键/值解法替换整个命名空间和自定义元素内容不起作用。

2 个答案:

答案 0 :(得分:2)

您无法轻松实现指定的确切架构设计(details元素可以包含可变内容)。考虑XML验证器的工作原理:它需要预览<details>元素的内容以确定内容的类型。有一个相关的讨论here

但是,您可以通过两种方式之一实现类似的效果。在您控制整个架构的情况下,我建议选项2,因为它更简单(当扩展类型可能在外部文档中时,选项1更好,例如第三方架构)。

选项1:使用继承和多态

在XSD中使用继承和多态时,在XSD中定义一个抽象的ComplexType,然后定义一个或多个扩展它的类型。您将抽象类型指定为item元素的成员,但在XML文档中,您可以使用xsi:type属性指定具体类型。使用此技术,您的XML文档将如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<somedocument 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns="http://grocery.example.com/schema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <item cost="34">
    <name>Apple</name>
    <description>A delicious apple!</description>
    <details xsi:type="AppleDetails">
      <color>red</color>
      <cultivar>Braeburn</cultivar>
    </details>
  </item>
  <item cost="32">
    <name>Baked Beans</name>
    <description>A tin of beans.</description>
    <details xsi:type="BeansDetails">
      <manufacturer>Heinz</manufacturer>
      <size>440g</size>
    </details>
  </item>
</somedocument>

以下是上述示例的架构:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema 
  attributeFormDefault="unqualified" 
  elementFormDefault="qualified" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://grocery.example.com/schema"
  xmlns="http://grocery.example.com/schema">

  <xs:complexType name="ItemDetails" abstract="true" />

  <xs:complexType name="AppleDetails">
    <xs:complexContent>
      <xs:extension base="ItemDetails">
        <xs:sequence>
          <xs:element minOccurs="0" name="color" type="xs:string" />
          <xs:element minOccurs="0" name="cultivar" type="xs:string" />
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

  <xs:complexType name="BeansDetails">
    <xs:complexContent>
      <xs:extension base="ItemDetails">
        <xs:sequence>
          <xs:element minOccurs="0" name="manufacturer" type="xs:string" />
          <xs:element minOccurs="0" name="size" type="xs:string" />
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

  <xs:element name="somedocument">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="item">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="name" type="xs:string" />
              <xs:element name="description" type="xs:string" />
              <xs:element name="details" type="ItemDetails" />
            </xs:sequence>
            <xs:attribute name="cost" type="xs:unsignedByte" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

选项2:使用<xs:choice>

使用<xs:choice>元素时,可以指定可在<item>元素中使用的多个互斥元素。这是文档的样子:

<?xml version="1.0" encoding="utf-8" ?>
<somedocument
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns="http://grocery.example.com/schema/2">
  <item cost="34">
    <name>Apple</name>
    <description>A delicious apple!</description>
    <appleDetails>
      <color>red</color>
      <cultivar>Braeburn</cultivar>
    </appleDetails>
  </item>
  <item cost="32">
    <name>Baked Beans</name>
    <description>A tin of beans.</description>
    <beansDetails>
      <manufacturer>Heinz</manufacturer>
      <size>440g</size>
    </beansDetails>
  </item>
</somedocument>

相应的模式比选项1简单得多,但要求在<xs:choice>中使用之前预先定义所有项目详细信息类型。架构如下所示:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema
  attributeFormDefault="unqualified"
  elementFormDefault="qualified"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://grocery.example.com/schema/2"
  xmlns="http://grocery.example.com/schema/2">

  <xs:complexType name="AppleDetails">
      <xs:sequence>
        <xs:element minOccurs="0" name="color" type="xs:string" />
        <xs:element minOccurs="0" name="cultivar" type="xs:string" />
      </xs:sequence>
  </xs:complexType>

  <xs:complexType name="BeansDetails">
      <xs:sequence>
        <xs:element minOccurs="0" name="manufacturer" type="xs:string" />
        <xs:element minOccurs="0" name="size" type="xs:string" />
      </xs:sequence>
  </xs:complexType>

  <xs:element name="somedocument">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="item">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="name" type="xs:string" />
              <xs:element name="description" type="xs:string" />
              <xs:choice>
                <xs:element name="appleDetails" type="AppleDetails" />
                <xs:element name="beansDetails" type="BeansDetails" />
              </xs:choice>
            </xs:sequence>
            <xs:attribute name="cost" type="xs:unsignedByte" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

因为您控制整个架构,并且可以将所有内容保存在单个XSD架构文档中,所以更好的选择可能是选项2.

另外,请考虑如何对架构进行版本化(请参阅here以获取一些建议),以便您可以扩展架构并添加新项类型,而不会破坏向后兼容性。

答案 1 :(得分:1)

我希望我能正确理解这个问题......在<details>内,你想要允许来自任何命名空间的任何元素,但是如果这些元素在http://example.org/apples中(SIR我必须问你,请做你拥有EXAMPLE.ORG DOMAIN?:D),那么你想要验证那些。

很容易。

  1. 在主模式文件someschema.xsd中,执行以下操作:

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      targetNamespace="urn:someschema"
      elementFormDefault="qualified"
    >
      <xs:import namespace="http://example.org/apples" schemaLocation="apples.xsd" />
      <!-- rest of your schema here -->
      <xs:complexType name="details">
        <xs:sequence maxOccurs="unbounded">
          <xs:any minOccurs="0" processContents="lax" />
        </xs:sequence>
      </xs:complexType>
    </xs:schema>
    
  2. 创建apples.xsd

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      targetNamespace="http://example.org/apples"
      elementFormDefault="qualified"
    >
      <xs:element name="color">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:enumeration value="red" />
            <xs:enumeration value="green" />
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
      <xs:element name="cultivar">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:enumeration value="Braeburn" />
            <xs:enumeration value="Granny Smith" />
            <xs:enumeration value="McIntosh" />
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
    </xs:schema>
    
  3. ???

  4. 利润!
  5. 重要的是,对于那些你想要在<details>内验证的外部名称空间,你需要在模式的顶部导入相应的模式文件。

    这实际上利用了这样一个事实,即XML Schema完全被延迟,甚至无法定义文档的根元素。从理论上讲,您也可以省略<import>,而只是针对someschema.xsdapples.xsd验证您的文档(尽管尚未对此进行测试)。