本文内容包括:
有效的 XML 模式不一定在 WSDL 定义中有效。了解 XML 模式定义(XML Schema
Definition,XSD)对 Web 服务设计有何重大影响。我们还提供了一些示例 XSD 和 WSDL,并将对其进行简单的讨论。
引言
可以采用很多种方式来创建 XML 模式,以使其与 W3C XML 模式规范相符。不过,有效的模式并不一定意味着其对特定的
Web 服务描述语言(Web Service Description Language,WSDL)定义也有效。本文说明了如何为
Web 服务设计恰当的构造模式,以使其同时符合 W3C WSDL 规范 (WSDL v1.1) 和 Web 服务基本概要(WS-I
Basic Profile,BP v1.1)。
在 WSDL 定义中,存在两种消息绑定样式:Document 和远程过程调用(Remote Procedure
Call,RPC)。在 WS-I Basic Profile (BP) 中,对消息绑定样式进行了进一步限制。例如,SOAP 主体中的
RPC-literal 绑定必须引用 wsdl:part 中的“type”属性 (BP R2203),而 Document 绑定必须同“element”属性一起使用
(BP R2204)。属性“element”引用 XML 模式元素,而属性“type”则指示 XSD 中的 simpleType
和 complexType。简而言之,Document 样式的消息基于 XML 模式元素定义,而 RPC 消息则使用 XML 模式类型定义。
而且,只有全局级别的元素 (BP R2206) 和类型能够在 WSDL 定义中定义。这些元素和类型是
XSD 中的 <schema> 的直接子项。所有非直接子项组件都是本地的,通常嵌套在另一个模式组件中。此处的组件将引用模式元素、complexType
或 simpleType。
将在以下各个部分中讨论三个主要 XML 模式设计模式以及其他样式:
- Russian Doll(意为“俄罗斯娃娃”)
- Salami Slice(意为“意大利香肠片”)
- Venetian Blind(意为“软百叶窗”)
Russian Doll
Russian Doll 模式采用的是仅具有一个全局元素的嵌套结构,如以下示例中的元素“Employee”。所有其他组件都封装在根元素中,均已本地化。
清单 1. 采用 Russian Doll 模式的示例 XSD
<xs:element name="Employee">
<xs:complexType> <xs:sequence> <xs:element
name="ErpPerson"> <xs:complexType mixed="false">
<xs:sequence> <xs:element name="lastName"
type="xs:string"/> <xs:element name="firstName"
type="xs:string"/> <xs:element name="mName"
type="xs:string"/> </xs:sequence>
</xs:complexType> </xs:element> <xs:element
name="ErpAddress"> <xs:complexType mixed="false">
<xs:sequence> <xs:element name="streetNumber"
type="xs:string"/> <xs:element name="streetName"
type="xs:string"/> <xs:element name="suiteNumber"
type="xs:string"/> <xs:element name="city"
type="xs:string"/> <xs:element name="stateOrProvince"
type="xs:string"/> <xs:element name="country"
type="xs:string"/> <xs:element name="postalCode"
type="xs:string"/> </xs:sequence>
</xs:complexType> </xs:element> </xs:sequence>
</xs:complexType> </xs:element> |
在这种情况下,能在 WSDL 定义中引用的元素只有根元素“Employee”。因此,此模式仅能用于在
WSDL 中定义 Document 样式的 Employee 消息。下面列出了示例 WSDL <message> 和
<binding>,且具有名为 publishEmployeeService 的操作,此操作的输入消息基于 Employee
模式。
清单 2. Document 样式的 Employee 消息的 WSDL 定义
…
<wsdl:message name="publishEmployeeServiceRequest">
<wsdl:part name="employee" element="typeIn:Employee">
<wsdl:documentation>publish employee data</wsdl:documentation>
</wsdl:part>
</wsdl:message>
… <wsdl:binding … >
<soap:binding … />
<wsdl:operation name="publishEmployeeService">
<soap:operation style="document"/>
<wsdl:input name="employee">
<soap:body use="literal"/>
</wsdl:input>
…
</wsdl:operation>
</wsdl:binding> |
请注意,wsdl:binding 部分中的 wsdl:input "name"
属性的值与 wsdl:message 中的“name”属性的值相同,而后者引用了 Employee 元素。typeIn 是输入模式
targetNamespace 的前缀,而 Employee 为模式的根元素。
Russian Doll 模式提供了具有最低扩展性的模式结构。在上面列出的示例中,除了根元素之外的所有组件均已本地化,不能进行重用。此模式适合在
Document 样式的 WSDL 中使用,但并不适合 RPC 样式,因为未定义全局级别的类型。同样,“类型”也是 WS-I BP
对 RPC 绑定样式的一个限制。
此外,此模式还不用于 WSDL 中可能需要扩展元素的情况,如为消息定义使用模式元素 ErpAddress
时。ErpAddress 是 XML 模式中的一个元素,但是并不位于全局级别。
Salami Slice
顾名思义,Salami Slice 模式将 XML 模式划分为具有更多全局级别元素的“片”。此模式非常适合
Document 样式的 WSDL,其灵活性高于 Russian Doll 样式。以下模式包含与 Russian Doll 模式相同的
Employee 信息,但采用不同的方式对其进行表示。
清单 3. 采用 Slice pattern 模式的示例 XSD
<xs:element name="Employee">
<xs:complexType> <xs:sequence> <xs:element
ref="ErpPerson"/> <xs:element ref="ErpAddress"/>
</xs:sequence> </xs:complexType> </xs:element>
<xs:element name="ErpPerson"> <xs:complexType
mixed="false"> <xs:sequence> <xs:element
name="lastName" type="xs:string"/>
<xs:element name="firstName" type="xs:string"/>
<xs:element name="mName" type="xs:string"/>
</xs:sequence> </xs:complexType> </xs:element>
<xs:element name="ErpAddress"> <xs:complexType
mixed="false"> <xs:sequence> <xs:element
name="streetNumber" type="xs:string"/>
<xs:element name="streetName" type="xs:string"/>
<xs:element name="suiteNumber" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="stateOrProvince" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
<xs:element name="postalCode" type="xs:string"/>
</xs:sequence> </xs:complexType> </xs:element> |
ErpPerson 和 ErpAddress 元素实际上可以进一步进行“切分”,以获得更多的元素粒度级别,但为了简单起见,我们仅到此为止。
现在 ErpPerson 和 ErpAddress 成为了模式中的全局级别元素,是能够在其他组件(如元素“Employee”)中引用的可重用组件。而且,使用
Salami Slice 模式的一个好处就是可以在 WSDL 定义中引用更多的元素。例如,现在可以在 WSDL 中将 ErpPerson
和 ErpAddress 分别用于为 ErpPerson 和 ErpAddress 有效负载定义 Document 样式的消息。
简要总结一下,Salami Slice 模式中的 Employee 模式可用于使用以下消息定义
WSDL:
- Document 绑定样式的 Employee 消息
- Document 绑定样式的 ErpPerson 消息
- Document 绑定样式的 ErpAddress 消息
以下是上面所列出的内容对应的 WSDL 消息和绑定定义:
清单 4. Document 样式的 Employee 消息的 WSDL 定义
… <wsdl:message name="publishEmployeeServiceRequest">
<wsdl:part name="employee" element="typeIn:Employee">
<wsdl:documentation>publish employee information</wsdl:documentation>
</wsdl:part> </wsdl:message>
… <wsdl:binding … > <soap:binding … />
<wsdl:operation name="publishEmployeeService">
<soap:operation style="document"/> <wsdl:input
name="employee"> <soap:body use="literal"/>
</wsdl:input>
…. </wsdl:operation> </wsdl:binding> |
清单 5. Document 样式的 ErpPerson 消息的 WSDL 定义
… <wsdl:message name="publishErpPersonServiceRequest">
<wsdl:part name="erpPerson" element="typeIn:ErpPerson">
<wsdl:documentation>publish employee data</wsdl:documentation>
</wsdl:part> </wsdl:message>
… <wsdl:binding … > <soap:binding … />
<wsdl:operation name="publishEmployeeService">
<soap:operation style="document"/> <wsdl:input
name="erpPerson"> <soap:body use="literal"/>
</wsdl:input>
…. </wsdl:operation> </wsdl:binding> |
清单 6. Document 样式的 ErpAddress 消息的
WSDL 定义
… <wsdl:message name="publishErpAddressServiceRequest">
<wsdl:part name="erpAddress" element="typeIn:ErpAddress">
<wsdl:documentation>publish employee address data</wsdl:documentation>
</wsdl:part> </wsdl:message>
… <wsdl:binding … > <soap:binding … />
<wsdl:operation name="publishErpAddressService">
<soap:operation style="document"/> <wsdl:input
name="erpAddress"> <soap:body use="literal"/>
</wsdl:input>
… </wsdl:operation> </wsdl:binding> |
不过,Salami Slice 模式并不适用于 RPC 样式的 WSDL,因为其中未定义全局级别的类型(简单类型或复杂类型)。
Venetian Blind
在此模式中,模式中除根元素外的所有全局级别的组件都定义为类型。Venetian Blind 模式与
Russian Doll 类似,因为都使用的是单个全局元素。Venetian Blind 和 Salami Slice 模式的区别在于,所有全局组件(除根元素外)现在都定义为类型,而不是像
Salami Slice 模式中一样定义为模式元素。
Venetian Blind 通常可以用于 Document 和 RPC 绑定类型。以下模式表示了与之前的示例相同的
Employee 信息,但采用的是 Venetian Blind 模式。除了根元素“Employee”外,全局级别的复杂类型定义如下:
清单 7. 采用 Venetian Blind 模式的示例 XSD
<xs:element name="Employee"
type="EmployeeType"/> <xs:complexType name="EmployeeType">
<xs:sequence> <xs:element name="ErpPerson"
type="ErpPersonType"/> <xs:element name="ErpAddress"
type="ErpAddressType"/> </xs:sequence>
</xs:complexType> <xs:complexType name="ErpPersonType">
<xs:sequence> <xs:element name="lastName"
type="xs:string"/> <xs:element name="firstName"
type="xs:string"/> <xs:element name="mName"
type="xs:string"/> </xs:sequence>
</xs:complexType> <xs:complexType name="ErpAddressType">
<xs:sequence> <xs:element name="streetNumber"
type="xs:string"/> <xs:element name="streetName"
type="xs:string"/> <xs:element name="suiteNumber"
type="xs:string"/> <xs:element name="city"
type="xs:string"/> <xs:element name="stateOrProvince"
type="xs:string"/> <xs:element name="country"
type="xs:string"/> <xs:element name="postalCode"
type="xs:string"/> </xs:sequence>
</xs:complexType> |
此模式可以用于在 WSDL 中定义 Document 和 RPC 样式的 Employee
消息,因为其中既包括全局级别的元素,也包括类型。不过,它并不支持 ErpPerson 和 ErpAddress 的 Document
样式的消息,因为二者都仅定义为了“complexType”。采用 Venetian Blind 模式的 Employee 模式可用于使用以下消息定义
WSDL:
- Document 绑定样式的 Employee 消息
- RPC 绑定样式的 Employee 消息
- RPC 绑定样式的 ErpPerson 消息
- RPC 绑定样式的 ErpAddress 消息
对应的 WSDL <message> 和 <binding> 定义如下:
清单 8. Document 样式的 Employee 消息的 WSDL 定义
… <wsdl:message name="publishEmployeeServiceRequest">
<wsdl:part name="employee" element="typeIn:Employee">
<wsdl:documentation>publish employee information</wsdl:documentation>
</wsdl:part> </wsdl:message>
… <wsdl:binding … > <soap:binding … />
<wsdl:operation name="publishEmployeeService">
<soap:operation style="document"/> <wsdl:input
name="employee"> <soap:body use="literal"/>
</wsdl:input>
…. </wsdl:operation> </wsdl:binding> |
清单 9. RPC 样式的 Employee 消息的 WSDL 定义
… <wsdl:message name="publishEmployeeServiceRequest">
<wsdl:part name="employee" type="typeIn:EmployeeType">
<wsdl:documentation>publish employee information</wsdl:documentation>
</wsdl:part> </wsdl:message>
… <wsdl:binding … > <soap:binding … />
<wsdl:operation name="publishEmployeeService">
<soap:operation style="rpc"/> <wsdl:input
name="employee"> <soap:body use="literal"
namespace="http://someCompany.com/soa/2006-07-01/publishEmployeeService"/>
</wsdl:input>
…. </wsdl:operation> </wsdl:binding> |
清单 10. RPC 样式的 ErpPerson 消息的 WSDL 定义
… <wsdl:message name="publishErpPersonServiceRequest">
<wsdl:part name="erpPerson" type="typeIn:ErpPersonType">
<wsdl:documentation>publish employee data</wsdl:documentation>
</wsdl:part> </wsdl:message>
… <wsdl:binding … > <soap:binding … />
<wsdl:operation name="publishErpPersonService">
<soap:operation style="rpc"/> <wsdl:input
name="erpPerson"> <soap:body use="literal"
namespace="http://someCompany.com/soa/2006-07-01/publishErpPersonService"/>
</wsdl:input>
…. </wsdl:operation> </wsdl:binding> |
清单 11. RPC 样式的 ErpAddress 消息的 WSDL
定义
… <wsdl:message name="publishErpAddressServiceRequest">
<wsdl:part name="erpAddress" type="typeIn:ErpAddressType">
<wsdl:documentation>publish employee address data</wsdl:documentation>
</wsdl:part> </wsdl:message>
… <wsdl:binding … > <soap:binding … />
<wsdl:operation name="publishErpAddressService">
<soap:operation style="rpc"/> <wsdl:input
name="erpAddress"> <soap:body use="literal"
namespace="http://someCompany.com/soa/2006-07-01/publishErpAddressService"/>
</wsdl:input>
.... </wsdl:operation> </wsdl:binding> |
尽管 ErpPerson 和 ErpAddress complexType 都可以在 RPC
样式的 WSDL 中引用,但将其定义为没有对应模式元素的 complexType 的做法并不太好,因为 complexType
无法由有效负载进行实例化。
例如,模式中的 ErpPersonType complexType 为 ErpPerson 提供了一个数据类型,包括
lastName, firstName 和 mName。它旨在用于创建以下所示的 XML 有效负载:
清单 12. 示例 ErpPerson 消息有效负载
<ErpPerson> <lastName>Smith</lastName>
<firstName>John</firstName> <mName>K</mName>
</ErpPerson> |
不过,Venetian Blind 样式的 XML 模式无法用于生成或验证此类有效负载,因为在模式中缺乏匹配的全局元素。为了在这种情况下解决此问题,请同时为
ErpPerson 定义元素和 complexType。
混合模式
您并不必完整地定义以上列出的所有三种模式。可以在需要的情况下混合使用这些模式。例如,某些组件可以定义为元素,而其他的仅定义为
complexType。从 XML 有效负载的角度而言,一个原则是,如果组件需要在 XML 有效负载中进行实例化,则应该将其定义为元素。否则,就可以将其定义为类型。
从 WSDL 的角度而言,如果组件在 Document 绑定样式中使用,则应将其定义为元素。否则就可以将模式组件定义为用于
RPC 绑定的类型。下面列出的模式满足了 WSDL 定义的以下要求:
- Document 绑定样式的 Employee 消息
- RPC 绑定样式的 Employee 消息
- Document 绑定样式的 ErpPerson 消息
- RPC 绑定样式的 ErpPerson 消息
- Document 绑定样式 ErpAddress 消息
清单 13. 采用混合模式的示例 XSD
<xs:element name="Employee"
type="EmployeeType"/>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element ref="ErpAddress"/>
<xs:element ref="ErpAddress"/>
</xs:sequence>
</xs:complexType>
<xs:element name="ErpPerson"
type="ErpPersonType"/>
<xs:complexType name="ErpPersonType">
<xs:sequence>
<xs:element name="lastName" type="xs:string"/>
<xs:element name="firstName" type="xs:string"/>
<xs:element name="mName" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:element name="ErpAddress">
<xs:complexType>
<xs:sequence>
<xs:element name="streetNumber" type="xs:string"/>
<xs:element name="streetName" type="xs:string"/>
<xs:element name="suiteNumber" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="stateOrProvince" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
<xs:element name="postalCode" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element> |
就 WSDL 中使用的 XML 模式元素和类型的可访问性而言,混合模式更为灵活。它允许您通过准确遵循在
Web 服务中选择的绑定样式构建 XML 模式。
仅类型
有时候,会仅在 XML 模式中定义全局级别的类型,而不定义任何全局元素。这种类型的模式通常作为基础模式使用,用以封装要在其他模式中引用的数据类型。以下是仅在全局级别包含了
complexType 的 Employee 模式:
清单 14. 仅具有全局级别 complexType 的示例 XSD
<xs:complexType name="EmployeeType">
<xs:sequence> <xs:element name="ErpPerson"
type="ErpPersonType"/> <xs:element name="ErpAddress"
type="ErpAddressType"/> </xs:sequence>
</xs:complexType> <xs:complexType name="ErpPersonType">
<xs:sequence> <xs:element name="lastName"
type="xs:string"/> <xs:element name="firstName"
type="xs:string"/> <xs:element name="mName"
type="xs:string"/> </xs:sequence>
</xs:complexType> <xs:complexType name="ErpAddressType">
<xs:sequence> <xs:element name="streetNumber"
type="xs:string"/> <xs:element name="streetName"
type="xs:string"/> <xs:element name="suiteNumber"
type="xs:string"/> <xs:element name="city"
type="xs:string"/> <xs:element name="stateOrProvince"
type="xs:string"/> <xs:element name="country"
type="xs:string"/> <xs:element name="postalCode"
type="xs:string"/>
The corresponding WSDL <message> and <binding> definitions
are listed below:on </xs:sequence> </xs:complexType> |
根据 W3C 和 WS-I BP,此模式对 RPC 样式的 WSDL 定义有效。不过,没有全局元素的模式对
XML 有效负载生成和验证用处不大。
结束语
总而言之,在考虑 Web 服务设计时,需要恰当考虑模式的构造方式。如果 XML 模式要用于 Document
和 RPC 样式的 Web 服务,最好在模式的全局级别同时包含元素和类型。
参考资料
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
- 了解有关 WS-I Basic Profile 的详细信息。
- 阅读 Should it be an Element or a Type?
- 了解何时应在全局或本地级别声明元素或类型
- 阅读 developerWorks XML 专区的文章“创建灵活和可扩展的 XML 模式”。
- 跟踪最新的 developerWorks 技术事件与网络广播。
- 参阅演示文档“Using XML schemas effectively in WSDL
design”。
获得产品和技术
- 了解有关 W3C XML 模式规范的更多信息。
- 阅读 W3C WSDL 规范。
- 了解有关 WS-I 测试工具的信息。
|