其他解决方案非常适合使根元素递归。但是,为了使非根元素递归而不在过程中将其变成有效的根元素,需要一种稍微不同的方法。
假设您要定义一种 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类型,然后在message内部定义一个该类型的data元素:
<?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> 内——但除了一些工作重复之外,这没什么大不了的。