47

我需要创建一个 XML 模式来验证 XML 文档的树结构。我不确切知道树的出现次数或深度级别。

XML 示例:

<?xml version="1.0" encoding="utf-8"?>
<node>
  <attribute/>
  <node>
    <attribute/>
    <node/>      
  </node>
</node> 

验证它的最佳方法是什么?递归?

4

3 回答 3

68

如果您需要递归类型声明,这里有一个可能有帮助的示例:

<xs:schema id="XMLSchema1"
    targetNamespace="http://tempuri.org/XMLSchema1.xsd"
    elementFormDefault="qualified"
    xmlns="http://tempuri.org/XMLSchema1.xsd"
    xmlns:mstns="http://tempuri.org/XMLSchema1.xsd"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
  <xs:element name="node" type="nodeType"></xs:element>

  <xs:complexType name="nodeType">    
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
      <xs:element name="node" type="nodeType"></xs:element>
    </xs:sequence>
  </xs:complexType>

</xs:schema>

如您所见,这定义了一个递归模式,其中只有一个名为“node”的节点,其深度可以根据需要进行。

于 2008-09-29T14:45:42.323 回答
42

XSD 确实允许元素的递归。这是给你的样本

<xsd:element name="section">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element ref="title"/>
      <xsd:element ref="para" maxOccurs="unbounded"/>
      <xsd:element ref="section" minOccurs="0" maxOccurs="unbounded"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

如您所见,section 元素包含一个类型为 section 的子元素。

于 2008-09-29T14:44:07.210 回答
1

其他解决方案非常适合使根元素递归。但是,为了使非根元素递归而不在过程中将其变成有效的根元素,需要一种稍微不同的方法。

假设您要定义一种 XML 消息格式,用于在分布式应用程序中的节点之间交换结构化数据。它包含以下元素:

  • <message>- 根元素;
  • <from>- 消息的来源;
  • <to>- 消息的目的地;
  • <type>- 消息中编码的数据结构类型;
  • <data>- 消息中包含的数据。

为了支持复杂的数据类型,<data>是一个递归元素。这使得可以编写如下消息,例如geometry_msgs/TwistStamped向飞行的无人机发送消息,指定其线性和角(即旋转)速度:

<?xml version="1.0" encoding="utf-8"?>

<message xmlns="https://stackoverflow.com/message/1.0.0">
  <from>controller:8080</from>
  <to>drone:8080</to>
  <type>geometry_msgs/TwistStamped</type>
  <data name="header">
    <data name="seq">0</data>
    <data name="stamp">
      <data name="sec">1</data>
      <data name="nsec">0</data>
    </data>
    <data name="frame_id">base_link</data>
  </data>
  <data name="twist">
    <data name="linear">
      <data name="x">1.0</data>
      <data name="y">0</data>
      <data name="z">1.0</data>
    </data>
    <data name="angular">
      <data name="x">0.3</data>
      <data name="y">0</data>
      <data name="z">0</data>
    </data>
  </data>
</message>

我们可以很容易地编写一个 XML 模式来验证这种格式:

<?xml version="1.0" encoding="utf-8"?>

<xs:schema
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="https://stackoverflow.com/message/1.0.0"
  elementFormDefault="qualified"
  xmlns="https://stackoverflow.com/message/1.0.0"
>
  <xs:element name="data">
    <xs:complexType mixed="true">
      <xs:sequence>
        <xs:element ref="data" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
      <xs:attribute name="name" type="xs:string" use="required"/>
    </xs:complexType>
  </xs:element>

  <xs:element name="message">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="from" type="xs:string"/>
        <xs:element name="to" type="xs:string"/>
        <xs:element name="type" type="xs:string"/>
        <xs:element ref="data" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

上面架构的问题在于它创建<data>了一个根元素,这意味着它还验证了下面的文档:

<?xml version="1.0" encoding="utf-8"?>

<data xmlns="https://stackoverflow.com/message/1.0.0" name="twist">
  <data name="header">
    <data name="seq">0</data>
    <data name="stamp">
      <data name="sec">1</data>
      <data name="nsec">0</data>
    </data>
    <data name="frame_id">base_link</data>
  </data>
  <data name="twist">
    <data name="linear">
      <data name="x">1.0</data>
      <data name="y">0</data>
      <data name="z">1.0</data>
    </data>
    <data name="angular">
      <data name="x">0.3</data>
      <data name="y">0</data>
      <data name="z">0</data>
    </data>
  </data>
</data>

为了避免这种副作用,我们不是<data>直接在全局级别定义元素,而是先定义一个data类型,然后data在内部定义该类型的元素message

<?xml version="1.0" encoding="utf-8"?>

<xs:schema
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="https://stackoverflow.com/message/1.0.0"
  elementFormDefault="qualified"
  xmlns="https://stackoverflow.com/message/1.0.0"
>
  <xs:complexType name="data" mixed="true">
    <xs:sequence>
      <xs:element name="data" type="data" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="name" type="xs:string" use="required"/>
  </xs:complexType>

  <xs:element name="message">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="from" type="xs:string"/>
        <xs:element name="to" type="xs:string"/>
        <xs:element name="type" type="xs:string"/>
        <xs:element name="data" type="data" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

请注意,我们最终不得不定义<data>元素两次——一次在data类型内部,一次在内部<element>——但除了一些工作重复之外,这没有任何意义。

于 2020-07-24T13:00:53.407 回答