WSDL的Types栏和Messages栏中的XML Schema
WSDL数据类型是基于"XML Schema: Datatypes"(XSD)的,现在已经被W3C推荐。这一文档共有三个版本(1999,2000/10,2001),因此必须在namespace属性的<definitions>元素中指明所使用的是哪一个版本。
xmlns:xsd="http://www.w3.org/2001/XMLSchema" |
在本文中,我将只考虑2001版本。WSDL标准的推荐者强烈建议使用2001版。
在本栏和以后各部分,需使用以下简缩或前缀
前缀 | 代表的Namespace | 描述 |
Soapenc | http://schemas.xmlsoap.org/soap/encoding | SOAP 1.1 encoding |
Wsdl | http://schemas.xmlsoap.org/wsdl/soap | WSDL 1.1 |
Xsd | http://www.w3.org/2001/XMLSchema | XML Schema |
XSD基类型
下表是直接从MSTK2文档中取出的,列举了MSTK2所支持的所有XSD基类型。它也告诉在客户端或服务器端的WSDL读取程序如何把XSD类型映射到在VB、C++和IDL中相应的类型。
XSD (Soap)类型 | 变量类型 | VB | C++ | IDL | Comments |
anyURI | VT_BSTR | String | BSTR | BSTR | |
base64Binary | VT_ARRAY | VT_UI1 | Byte() | SAFEARRAY | SAFEARRAY(unsigned char) | |
Boolean | VT_BOOL | Boolean | VARIANT_BOOL | VARIANT_BOOL | |
Byte | VT_I2 | Integer | short | short | 转换时验证范围有效性 |
Date | VT_DATE | Date | DATE | DATE | 时间设为 oo:oo:oo |
DateTime | VT_DATE | Date | DATE | DATE | |
Double | VT_R8 | Double | double | double | |
Duration | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
ENTITIES | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
ENTITY | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
Float | VT_R4 | Single | float | float | |
GDay | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
GMonth | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
GMonthDay | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
GYear | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
GYearMonth | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
ID | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
IDREF | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
IDREFS | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
Int | VT_I4 | Long | long | long | |
Integer | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 转换时范围生效 |
Language | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
Long | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 转换时范围生效 |
Name | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
NCName | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
negativeInteger | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 转换时范围生效 |
NMTOKEN | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
NMTOKENS | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
nonNegativeIntege | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 转换时范围生效 |
nonPositiveInteger | VT_DECIMAL | Variant | DECIMA | DECIMAL | 转换时范围生效 |
normalizedString | VT_BSTR | String | BSTR | BSTR | |
NOTATION | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
Number | VT_DECIMAL | Variant | DECIMAL | DECIMAL | |
positiveInteger | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 转换时范围生效 |
Qname | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
Short | VT_I2 | Integer | short | short | |
String | VT_BSTR | String | BSTR | BSTR | |
Time | VT_DATE | Date | DATE | DATE | 日设为1899年12月30日 |
Token | VT_BSTR | String | BSTR | BSTR | 不转换和生效 |
unsignedByte | VT_UI1 | Byte | unsigned char | unsigned char | |
UnsignedInt | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 转换时范围生效 |
unsignedLong | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 转换时范围生效 |
unsignedShort | VT_UI4 | Long | Long | Long | 转换时范围生效 |
XSD定义了两套内建的数据类型:原始的和派生的。在下文中查阅内建数据类型的层次十分有益:
http://www.w3.org/TR/2001/PR-xmlschema-2-20010330complex类型 XML schema允许complex类型的定义,就像C里是struct。例如,为了定义类似如下的C的struct类型:
我们可以写XML schema:
不过,complex类型可以表达比struct更多的信息。除了<sequence>以外,它还可以有其他的子元素,比如<all>
这意味着<element>的成员变量可以以任何顺序排列,每一个都是可选的。这和C中的struct类型不太一样。 注意内建数据类型string, int, float。C的string也是XML的string,float也类似。但C中的long类型在XML中是int(上表中)。 在WSDL文件中,像上面的complex类型可以在Types栏声明。例如,我可以用以下方式声明PERSON类型并用在Messages栏。
上例中第一个消息由"adperson",并且有一个<part>,其类型为"PERSON"。PERSON类型是在Types栏声明的。 如果我们使用完整的WSDL文件包含以上的部分,并以之初始化MSTK2 SoapClient,它将成功的解析该文件。当然,它不会去调用<addPerson>。这是因为SoapClient本身并不知道如何处理complex类型,它需要定制类型映射来处理complex类型。MSTK2文档中有包含定制类型映射的示例。 还有另一种方法可以把<part>元素联系到类型声明。这就是使用元素。下例中我将Types栏中声明两个元素("Person"和"Gendr"),然后我将在"addPerson"<message>中使用元素属性来引用它们。
Types栏中的Gender<element>里嵌入了枚举类型,其枚举值为"Male""Female"。然后我又在"addPerson"<message>中通过元素属性而不是类型属性来引用它。 "元素属性"和"类型属性"在把某特定类型关联到<part>时有什么不同呢?使用元素属性,我们可以描述一个部分,它可以假定几个类型(就像变量一样),而是用类型属性我们就无法这样做。下例说明了这一点。
上例也告诉我们extension的派生。"femailPerson"和"malePerson"都是从"PERSON"派生出来的。它们各有一些额外的元素:"femalePerson"有"favoriteLipstick"元素,"malePerson"有"favoriteShavingLotion"元素。两派生类型都归入一个complex类型"maleOrFemalePerson",使用的是<choice>构造。最后,在"adperson"<message>中,新类型有"person"<part>引用。这样,参数或<part>就可以是"femalePerson"或"malePerson"了。 数组 XSD提供<list>结构来声明一个数组,元素之间有空格界定。不过SOAP不是使用XSD来编码数组的,它定义了自己的数组类型--"SOAP-ENC: Array"。下列的例子揭示了从这一类型派生出一位整数数组的方法:
新的complex类型从soapenc:array限制派生。然后又声明了complex类型的一个属性。引用"soapenc:arrayType"实际上是这样完成的:
wsdl:arrayType属性值决定了数组每个成员的类型。数组的成员也可以是Complex类型。:
WSDL要求数组的类型由"ArrayOf"和每个数组元素的类型串联而成。很显然,顾名思义,"ArrayOfPERSON"是PERSON结构的数组。下面我将使用ArrayOfPERSON来声明一个<message>,并加入不止一个PERSON:
PortType定义了一些抽象的操作。PortType中的operation元素定义了调用PortType中所有方法的语法,每一个operation元素声明了方法的名称、参数(使用<message>元素)和各自的类型(<part>元素要在所有<message>中声明)。 在一篇WSDL文档中可以有几个<PortType>元素,每一个都和一些相关操作放在一起,就和COM和一组操作的接口相似。 在<operation>元素中,可能会有至多一个<input>元素,一个<output>元素,以及一个<fault>元素。三个元素各有一个名字和一个消息属性。 <input>, <output>, <fault>元素属性的名字有何含义呢?它们可以用来区别两个同名操作(重载)。例如,看下面两个C函数:
这种重载在WSDL中可以这样表示:
到目前为止,还没有一种SOAP的实现支持重载。这对基于JAVA的客户端十分重要,因为JAVA服务器使用的接口用到JAVA的重载特性。而对基于COM的客户端,就不那么重要,因为COM是不支持重载的。 <binding>和<operation>元素 Binding栏是完整描述协议、序列化和编码的地方,Types, Messages和PortType栏处理抽象的数据内容,而Binding栏是处理数据传输的物理实现。Binding栏把前三部分的抽象定义具体化。 把相关的数据制定和消息声明分开,这意味着同一类型服务的提供者可以把一系列的操作标准化。每个提供者可以提供定制的binding来互相区分。WSDL也有一个重要的结构,使抽象定义可以放在分离的文件中,而不是和Bindings和Services在一起,这样可在不同的服务提供者之间提供标准化的抽象定义,这很有帮助。例如,银行可以用WSDL文档来标准化一些银行的操作。每个银行仍然可以自由的订制下层的协议、串行优化,及编码。 下面是重载的WSDL示例 的Binding栏,重复在此以便讨论:
<binding>元素已经取了一个名字(本例中"fooSampleBinding"),这样就可以被Services栏的<port>元素引用了。它有一个"type"的属性引用<portType>,本例中就是"wsdlns:fooSamplePortType"。第二行是MSTK2的扩展元素<stk:binding>,它指定了preferredEncoding属性为"UTF-8"。 <soap:binding>元素指定了所使用的风格("rpc")和传输方式。Transport属性应用了一个namespace,正是这个namespace指明使用HTTP SOAP协议。 有两个同以"foo"命名的<operation>元素。唯一不同的是它们各自的<input>名字,分别为"foo1"和"foo2"。两个<operation>元素中的<soap:operation>元素有同样的"soapAction"属性,是URI。soapAction属性是SOAP特定的URI,它只是简单的使用于SOAP消息。所产生的SOAP消息有一个SOAPAction头,而URI也仅在<soap:operation>元素里才起作用。soapAction属性在HTTP的binding中是必需的,但在其他非HTTP binding中却不要提供。目前它的使用并不清楚,但它似乎有助于本例中的两个"foo"操作。SOAP 1.1指明soapAction用来确定消息的"意图"。似乎服务器可以在不解析整个消息的情况下就能使用这一属性来发送消息。实际上,它的使用多种多样。<soap:operation>元素也可以包含另一属性,即"style"属性,在有必要冲突<soap:binding>元素指定的风格时可以使用。 <operation>属性可以包含<input>, <output> 和<fault>的元素,它们都对应于PortType栏中的相同元素。只有<input>元素在上例中提供。这三个元素中的每一个可有一个可选的"name"属性,在本例中,我们用这种方法来区分同名操作。在本例的<input>元素中有一个<soap:body>元素,它指定了哪些信息被写进SOAP消息的信息体中。该元素有以下属性: Use 用于制定数据是"encoded"还是"literal"。"Literal"指结果SOAP消息包含以抽象定义(Types, Messages, 和PortTypes)指定格式存在的数据。"Encoded"指"encodingStyle"属性决定了编码方式。 Namespace 每个SOAP消息体可以有其自己的namespace来防止命名冲突。这一属性制定的URI在结果SOAP消息中逐字使用。 EncodingStyle 对SOAP编码,它应该有以下URI值:
|
文档风格实现
在前几栏中,<soap:binding>元素有一个类型属性,设为"rpc"。此属性设为"document"时会改变传输时消息的串行化。不同于函数签名,现在的消息是文档传输的。在这类binding中,<message>元素定义文档格式,而不是函数签名。作为例子,考虑以下WSDL片段:
<definitions xmlns:stns="(SchemaTNS)" xmlns:wtns="(WsdlTNS)" targetNamespace="(WsdlTNS)"> <schema targetNamespace="(SchemaTNS)" elementFormDefault="qualified"> <element name="SimpleElement" type="xsd:int"/> <element name="CompositElement" type="stns:CompositeType"/> <complexType name="CompositeType"> <all> <element name='a' type="xsd:int"/> <element name='b' type="xsd:string"/> </all> </complexType> </schema> <message...> <part name='p1' type="stns:CompositeType"/> <part name='p2' type="xsd:int"/> <part name='p3' element="stns:SimpleElement"/> <part name='p4' element="stns:CompositeElement"/> </message> … </definitions> |
schema有两个元素:SimpleElement和CompositeElement,还有一个类型声明(CompositeType)。唯一声明的<message>元素有四个部分:p1:Composite型;p2:int型;p3:SimpleElement型;p4:CompositeElement型。以下有一个表,对四种类型的use/type决定的binding作一比较:rpc/literal, document/literal, rpc/encoded, 以及document/encoded。表指明了每种binding的表现。
<service>和<port>元素
service是一套<port>元素。在一一对应形式下,每个<port>元素都和一个location关联。如果同一个<binding>有多个<port>元素与之关联,可以使用额外的URL地址作为替换。
一个WSDL文档中可以有多个<service>元素,而且多个<service>元素十分有用,其中之一就是可以根据目标URL来组织端口。这样,我就可以方便的使用另一个<service>来重定向我的股市查询申请。我的客户端程序仍然工作,因为这种根据协议归类的服务不随服务而变化。多个<service>元素的另一个作用是根据特定的协议划分端口。例如,我可以把所有的HTTP端口放在同一个<service>中,所有的SMTP端口放在另一个<service>里。我的客户可以搜索与它可以处理的协议相匹配的<service>。
<service name="FOOService"> <port name="fooSamplePort" binding="fooSampleBinding"> <soap:address location="http://carlos:8080/fooService/foo.asp"/> </port> </service> |
在一个WSDL文档中,<service>的name属性用来区分不同的service。因为同一个service中可以有多个端口,它们也有"name"属性。
总结
本文中我描述了WSDL文档关于SOAP方面的最显著的特点。不过应该说明的是WSDL并不仅限于HTTP上的SOAP。WSDL用来描述HTTP-POST、HTTP-GET、SMTP及其他协议时非常清晰。使用了WSDL,SOAP更加容易处理了,无论是开发者还是使用者。我相信WSDL和SOAP一起将会开创网络应用程序世界的新时代。
WSDL的namespace里有一系列的XML元素。下表概述了那些元素、它们的属性和内容。
元素 | 属性 | 内容(子元素) |
<definitions> | name targetNamespace xmlns (other namespaces) | <types> <message> <portType> <binding> <service> |
<types> | (none) | <xsd:schema> |
<message> | Name | <part> |
<portType> | Name | <operation> |
<binding> | name type | <operation> |
<service> | name | <port> |
<part> | name type | (empty) |
<operation> | name parameterOrder | <input> <output> <fault> |
<input> | name message | (empty) |
<output> | name message | (empty) |
<fault> | name message | (empty) |
<port> | name binding | <soap:address> |
资源:
1. WSDL 1.1
2. SOAP 1.1
3. XML Schema Primer
4. MS SOAP Toolkit Download Site
5. A tool for translating IDL to WSDL
6. Free Web Services resources including a WSDL to VB proxy generator
7. PocketSOAP: SOAP related components, tools & source code