SOAP消息
對于使用WSDL的客戶機和服務(wù)機來說,研究WSDL文件的一種方法就是決定什么來接受所發(fā)送的信息。盡管SOAP使用底層協(xié)議,如IP和HTTP等,但應(yīng)用程序決定了服務(wù)器與客戶機之間交互的高級協(xié)議。也就是說,進(jìn)行一項操作,比如"echoint"把輸入的整數(shù)送回,參數(shù)的數(shù)目、每個參數(shù)的類型、以及參數(shù)如何傳送等因素決定了應(yīng)用程序特定的協(xié)議。有很多方法可以確定此類協(xié)議,但我相信最好的方法就是使用WSDL。如果我們用這種視角來看待它,WSDL不只是一種接口協(xié)議,而且是一種協(xié)議特定的語言。它就是我們超越"固定"協(xié)議(IP、HTTP等)所需要的應(yīng)用程序特定協(xié)議。
WSDL可以確定SOAP消息是否遵從RPC或文檔風(fēng)格。RPC風(fēng)格的消息(就是示例中所用的)看起來像是函數(shù)調(diào)用。而文檔風(fēng)格的消息則更普通,嵌套層次更小。下面的XML消息就是示例WSDL文件解析后的發(fā)送/接受效果,解析使用的是MS SOAP Toolkit 2.0(MSTK2)中的SoapClient對象。
從客戶端調(diào)用"foo(5131953)"函數(shù):





















兩函數(shù)都調(diào)用了消息,其回應(yīng)是有效的XML。SOAP消息由幾部分組成,首先是<Envelop>元素,包含一個可選的<Header>元素以及至少一個<body>元素。Rpc函數(shù)所調(diào)用的消息體有一個根據(jù)操作"foo"命名的元素,而回應(yīng)信息體有一個"fooResponse"元素。Foo元素有一個部分<arg>,就和WSDL中描述的一樣,是單參數(shù)的。fooResponse也相應(yīng)的有一個<result>的部分。注意encodingStyle、envelope和message的namespace和WSDL Bindings欄中的預(yù)定義的一致,重復(fù)如下:





















WSDL的Types欄和Messages欄中的XML Schema
WSDL數(shù)據(jù)類型是基于"XML Schema: Datatypes"(XSD)的,現(xiàn)在已經(jīng)被W3C推薦。這一文檔共有三個版本(1999,2000/10,2001),因此必須在namespace屬性的<definitions>元素中指明所使用的是哪一個版本。

在本欄和以后各部分,需使用以下簡縮或前綴
前綴 | 代表的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基類型。它也告訴在客戶端或服務(wù)器端的WSDL讀取程序如何把XSD類型映射到在VB、C++和IDL中相應(yīng)的類型。
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 | 轉(zhuǎn)換時驗證范圍有效性 |
Date | VT_DATE | Date | DATE | DATE | 時間設(shè)為 oo:oo:oo |
DateTime | VT_DATE | Date | DATE | DATE | ? |
Double | VT_R8 | Double | double | double | ? |
Duration | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
ENTITIES | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
ENTITY | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
Float | VT_R4 | Single | float | float | ? |
GDay | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
GMonth | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
GMonthDay | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
GYear | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
GYearMonth | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
ID | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
IDREF | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
IDREFS | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
Int | VT_I4 | Long | long | long | ? |
Integer | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 轉(zhuǎn)換時范圍生效 |
Language | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
Long | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 轉(zhuǎn)換時范圍生效 |
Name | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
NCName | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
negativeInteger | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 轉(zhuǎn)換時范圍生效 |
NMTOKEN | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
NMTOKENS | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
nonNegativeIntege | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 轉(zhuǎn)換時范圍生效 |
nonPositiveInteger | VT_DECIMAL | Variant | DECIMA | DECIMAL | 轉(zhuǎn)換時范圍生效 |
normalizedString | VT_BSTR | String | BSTR | BSTR | ? |
NOTATION | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
Number | VT_DECIMAL | Variant | DECIMAL | DECIMAL | ? |
positiveInteger | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 轉(zhuǎn)換時范圍生效 |
Qname | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
Short | VT_I2 | Integer | short | short | ? |
String | VT_BSTR | String | BSTR | BSTR | ? |
Time | VT_DATE | Date | DATE | DATE | 日設(shè)為1899年12月30日 |
Token | VT_BSTR | String | BSTR | BSTR | 不轉(zhuǎn)換和生效 |
unsignedByte | VT_UI1 | Byte | unsigned char | unsigned char | ? |
UnsignedInt | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 轉(zhuǎn)換時范圍生效 |
unsignedLong | VT_DECIMAL | Variant | DECIMAL | DECIMAL | 轉(zhuǎn)換時范圍生效 |
unsignedShort | VT_UI4 | Long | Long | Long | 轉(zhuǎn)換時范圍生效 |
XSD定義了兩套內(nèi)建的數(shù)據(jù)類型:原始的和派生的。在下文中查閱內(nèi)建數(shù)據(jù)類型的層次十分有益:


complex類型
XML schema允許complex類型的定義,就像C里是struct。例如,為了定義類似如下的C的struct類型:
string?firstName;
string?lastName;
long?ageInYears;
float?weightInLbs;
float?heightInInches;
}?PERSON;
我們可以寫XML schema:



















這意味著<element>的成員變量可以以任何順序排列,每一個都是可選的。這和C中的struct類型不太一樣。
注意內(nèi)建數(shù)據(jù)類型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,它將成功的解析該文件。當(dāng)然,它不會去調(diào)用<addPerson>。這是因為SoapClient本身并不知道如何處理complex類型,它需要定制類型映射來處理complex類型。MSTK2文檔中有包含定制類型映射的示例。
還有另一種方法可以把<part>元素聯(lián)系到類型聲明。這就是使用元素。下例中我將Types欄中聲明兩個元素("Person"和"Gendr"),然后我將在"addPerson"<message>中使用元素屬性來引用它們。




































Types欄中的Gender<element>里嵌入了枚舉類型,其枚舉值為"Male""Female"。然后我又在"addPerson"<message>中通過元素屬性而不是類型屬性來引用它。
"元素屬性"和"類型屬性"在把某特定類型關(guān)聯(lián)到<part>時有什么不同呢?使用元素屬性,我們可以描述一個部分,它可以假定幾個類型(就像變量一樣),而是用類型屬性我們就無法這樣做。下例說明了這一點。













































上例也告訴我們extension的派生。"femailPerson"和"malePerson"都是從"PERSON"派生出來的。它們各有一些額外的元素:"femalePerson"有"favoriteLipstick"元素,"malePerson"有"favoriteShavingLotion"元素。兩派生類型都?xì)w入一個complex類型"maleOrFemalePerson",使用的是<choice>構(gòu)造。最后,在"adperson"<message>中,新類型有"person"<part>引用。這樣,參數(shù)或<part>就可以是"femalePerson"或"malePerson"了。
數(shù)組
XSD提供<list>結(jié)構(gòu)來聲明一個數(shù)組,元素之間有空格界定。不過SOAP不是使用XSD來編碼數(shù)組的,它定義了自己的數(shù)組類型--"SOAP-ENC: Array"。下列的例子揭示了從這一類型派生出一位整數(shù)數(shù)組的方法:







新的complex類型從soapenc:array限制派生。然后又聲明了complex類型的一個屬性。引用"soapenc:arrayType"實際上是這樣完成的:

wsdl:arrayType屬性值決定了數(shù)組每個成員的類型。數(shù)組的成員也可以是Complex類型。:








WSDL要求數(shù)組的類型由"ArrayOf"和每個數(shù)組元素的類型串聯(lián)而成。很顯然,顧名思義,"ArrayOfPERSON"是PERSON結(jié)構(gòu)的數(shù)組。下面我將使用ArrayOfPERSON來聲明一個<message>,并加入不止一個PERSON:


































?