直播中
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
本文僅以 2001 版為考量。WSDL 標(biāo)準(zhǔn)的擁護(hù)者,也大力建議使用 2001 版。
在本節(jié)與後續(xù)章節(jié)中,採用的字首或命名空間速記法如下:
字首 對應(yīng)的命名空間 說明
soapenc http://schemas.xmlsoap.org/soap/encoding SOAP 1.1 編碼
wsdl http://schemas.xmlsoap.org/wsdl/soap WSDL 1.1
xsd http://www.w3.org/2001/XMLSchema XML Schema
XSD 基本類型
下表直接取自 MSTK2 文件,列舉了 MSTK2 支援的所有 XSD 基本類型。該表說明,位於客戶端與伺服端的 WSDL 讀者,如何在 VB、C++、與 IDL 中,將 XSD 類型對應(yīng)至不同與對等的類型。
XSD (Soap) 類型 不同的類型 VB C++ IDL 註解
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)換時驗證範(fà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 不執(zhí)行驗證或轉(zhuǎn)換
ENTITIES VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
ENTITY VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
float VT_R4 Single float float
gDay VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
gMonth VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
gMonthDay VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
gYear VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
gYearMonth VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
ID VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
IDREF VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
IDREFS VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
int VT_I4 long long long
integer VT_DECIMAL Variant DECIMAL DECIMAL 轉(zhuǎn)換時驗證範(fàn)圍。
language VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
long VT_DECIMAL Variant DECIMAL DECIMAL 轉(zhuǎn)換時驗證範(fàn)圍。
Name VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
NCName VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
negativeInteger VT_DECIMAL Variant DECIMAL DECIMAL 轉(zhuǎn)換時驗證範(fàn)圍。
NMTOKEN VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
NMTOKENS VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
nonNegativeInteger VT_DECIMAL Variant DECIMAL DECIMAL 轉(zhuǎn)換時驗證範(fàn)圍。
nonPositiveInteger VT_DECIMAL Variant DECIMAL DECIMAL 轉(zhuǎn)換時驗證範(fàn)圍。
normalizedString VT_BSTR String BSTR BSTR
NOTATION VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
number VT_DECIMAL Variant DECIMAL DECIMAL
positiveInteger VT_DECIMAL Variant DECIMAL DECIMAL 轉(zhuǎn)換時驗證範(fàn)圍。
QName VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
short VT_I2 Integer short short
string VT_BSTR String BSTR BSTR
time VT_DATE Date DATE DATE Day 設(shè)定成 1899 年 12 月 30 日
token VT_BSTR String BSTR BSTR 不執(zhí)行驗證或轉(zhuǎn)換
unsignedByte VT_UI1 Byte unsigned char unsigned char
unsignedInt VT_DECIMAL Variant DECIMAL DECIMAL 轉(zhuǎn)換時驗證範(fàn)圍。
unsignedLong VT_DECIMAL Variant DECIMAL DECIMAL 轉(zhuǎn)換時驗證範(fàn)圍。
unsignedShort VT_UI4 long long long 轉(zhuǎn)換時驗證範(fàn)圍。
XSD 定義兩組內(nèi)建的資料類型:基本類型與衍生類型。若需進(jìn)一步資訊,可前往 http://www.w3.org/TR/2001/PR-xmlschema-2-20010330,檢視內(nèi)建類型的階層架構(gòu)。
複雜類型
XML Schema 可定義複雜類型,也就是 C 中的 struct。例如,下列 C struct 的相對定義方式為:
typedef struct {
string firstName;
string lastName;
long ageInYears;
float weightInLbs;
float heightInInches;
} PERSON;
若使用 XML Schema 可以撰寫成:
<xsd:complexType name="PERSON">
<xsd:sequence>
<xsd:element name="firstName" type="xsd:string"/>
<xsd:element name="lastName" type="xsd:string"/>
<xsd:element name="ageInYears" type="xsd:int"/>
<xsd:element name="weightInLbs" type="xsd:float"/>
<xsd:element name="heightInInches" type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
不過,<complexType> 所能表示的,絕不僅止於 struct 的對應(yīng)而已。除了 <sequence> 之外,它還可以有其它的子元素。若不用 <sequence>,也可以使用 <all>:
<xsd:complexType name="PERSON">
<xsd:all>
<xsd:element name="firstName" type="xsd:string"/>
<xsd:element name="lastName" type="xsd:string"/>
<xsd:element name="ageInYears" type="xsd:int"/>
<xsd:element name="weightInLbs" type="xsd:float"/>
<xsd:element name="heightInInches" type="xsd:float"/>
</xsd:all>
</xsd:complexType>
其意義則是,成員變數(shù) <element> 可以任何順序輸入,且每一項都具有選擇性。這點便與 C struct 的使用方式不同了。
請注意範(fàn)例中,string (字串)、int (整數(shù))、float (浮點數(shù)) 等內(nèi)建資料類型的使用方式。C 的字串在 XML 中也是字串,且浮點數(shù)還是浮點數(shù)。但 C 的 long (長整數(shù)),在 XML 則是 int (請參考上表)。
在 WSDL 檔案中,Types 區(qū)段是宣告上述複雜類型的位置。例如,PERSON 類型可以下列方式宣告,並將其用於 Messages 區(qū)段中:
<?xml version="1.0" encoding="UTF-8" ?>
<definitions ?>
<types>
<schema targetNamespace="someNamespace"
xmlns:typens="someNamespace" >
<xsd:complexType name="PERSON">
<xsd:sequence>
<xsd:element name="firstName" type="xsd:string"/>
<xsd:element name="lastName" type="xsd:string"/>
<xsd:element name="ageInYears" type="xsd:int"/>
<xsd:element name="weightInLbs" type="xsd:float"/>
<xsd:element name="heightInInches" type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</schema>
</types>
<message name="addPerson">
<part name="person" type="typens:PERSON"/>
</message>
<message name="addPersonResponse">
<part name="result" type="xsd:int"/>
</message>
</definitions>
在上述的範(fàn)例中,第一個訊息的名稱是「addPerson」,它有個類型為「PERSON」的 <part>。在 Types 區(qū)段中,類型 PERSON 會被宣告為複雜類型。
起始 MSTK2 SoapClient 時,若在上述片段使用完整的 WSDL 檔案,它便可成功地剖析該檔案。不過,它還是不能將函數(shù)呼叫,傳送至 <addPerson>。這是因為 SoapClient 本身不知道如何處理複雜類型;它需要自訂的類型對應(yīng)器 (mapper) 才能處理複雜類型。在 MSTK2 文件中有一個範(fàn)例應(yīng)用程式,它含有自訂的類型對應(yīng)器。
另外還有一個方法,可將 <part> 元素,關(guān)連至類型宣告。這個方法使用的是元素,而非類型屬性。下個範(fàn)例會在 Types 區(qū)段中,先宣告兩個元素 (「Person」與「Gender」);然後在「addPerson」的 <message> 中,再使用元素屬性參照它們。
<?xml version="1.0" encoding="UTF-8" ?>
<definitions ?>
<types>
<schema targetNamespace="someNamespace"
xmlns:typens="someNamespace" >
<element name="Person">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="firstName" type="xsd:string"/>
<xsd:element name="lastName" type="xsd:string"/>
<xsd:element name="ageInYears" type="xsd:int"/>
<xsd:element name="weightInLbs" type="xsd:float"/>
<xsd:element name="heightInInches" type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
</element>
<element name="Gender">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Male" />
<xsd:enumeration value="Female" />
</xsd:restriction>
</xsd:simpleType>
</element>
</schema>
</types>
<message name="addPerson">
<part name="who" element="typens:Person"/>
<part name="sex" element="typens:Gender"/>
</message>
<message name="addPersonResponse">
<part name="result" type="xsd:int"/>
</message>
</definitions>
在 Types 區(qū)段的 Gender <element> 中,內(nèi)嵌著一個匿名的列舉類型,其值可以是「Male」或「Female」。然後在「addPerson」的 <message> 中,再使用元素屬性 (不用類型屬性) 參照該元素。
若欲關(guān)連特定類型至 <part>,使用「元素」與「類型」屬性有何不同?若使用「類型」屬性,part 可描述成能採取數(shù)種類型 (就像變數(shù)一樣);但若使用「元素」屬性,便不能這麼做。請參考下列範(fàn)例的說明。
<?xml version="1.0" encoding="UTF-8" ?>
<definitions ?>
<types>
<schema targetNamespace="someNamespace"
xmlns:typens="someNamespace">
<xsd:complexType name="PERSON">
<xsd:sequence>
<xsd:element name="firstName" type="xsd:string"/>
<xsd:element name="lastName" type="xsd:string"/>
<xsd:element name="ageInYears" type="xsd:int"/>
<xsd:element name="weightInLbs" type="xsd:float"/>
<xsd:element name="heightInInches" type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="femalePerson">
<xsd:complexContent>
<xsd:extension base="typens:PERSON" >
<xsd:element name="favoriteLipstick" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="malePerson">
<xsd:complexContent>
<xsd:extension base="typens:PERSON" >
<xsd:element name="favoriteShavingLotion" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="maleOrFemalePerson">
<xsd:choice>
<xsd:element name="fArg" type="typens:femalePerson" >
<xsd:element name="mArg" type="typens:malePerson" />
</xsd:choice>
</xsd:complexType>
</schema>
</types>
<message name="addPerson">
<part name="person" type="typens:maleOrFemalePerson"/>
</message>
<message name="addPersonResponse">
<part name="result" type="xsd:int"/>
</message>
</definitions>
此範(fàn)例也說明了副檔名的衍生用法。「femalePerson」與「malePerson」兩者,都是衍生自「PERSON」。它們都各有一個額外的元素:「femalePerson」的「favoriteLipstick」以及「malePerson」的「favoriteShavingLotion」。使用 <choice> 的 construct,這兩個衍生類型又可結(jié)合成一個複雜類型「maleOrFemalePerson」。最後,在「addPerson」的 <message> 中,此結(jié)合類型又可供「person」的 <part> 參照。而此 <part> 或參數(shù),可以是「femalePerson」或「malePerson」。
陣列
XSD 可提供 <list> construct,以宣告空白所分隔的項目陣列。但是,SOAP 並不使用 XSD 清單為陣列編碼;而是為陣列定義自己的類型,即「SOAP-ENC:Array」。下列範(fàn)例說明,如何為單一維度的整數(shù)陣列,依其法則導(dǎo)出此種類型:
<xsd:complexType name="ArrayOfInt">
<xsd:complexContent>
<xsd:restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:int[]"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
只要使用導(dǎo)出限制的方式,即可從 soapenc:Array 宣告新的複雜類型。接著便可宣告此複雜類型的屬性:arrayType。參照「soapenc:arrayType」實際上即是 arrayType 屬性的宣告,其方式如下:
<xsd:attribute name="arrayType" type="xsd:string"/>
然後,wsdl:arrayType 屬性值,可決定每個陣列成員的類型。陣列項目也可以是複雜類型:
<xsd:complexType name="ArrayOfPERSON">
<xsd:complexContent>
<xsd:restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType"
wsdl:arrayType="typens:PERSON[]"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
WSDL 的要求是,陣列的類型名稱必須是,「ArrayOf」與陣列項目類型的的串連 (concatenation)。也因此,單從名稱即可得知,「ArrayOfPERSON」是 PERSON struct 的陣列。在下例中,只要使用 ArrayOfPERSON 宣告一個 <message>,即可新增多個 PERSON:
<?xml version="1.0" encoding="UTF-8" ?>
<definitions ?>
<types>
<schema targetNamespace="someNamespace"
xmlns:typens="someNamespace" >
<xsd:complexType name="PERSON">
<xsd:sequence>
<xsd:element name="firstName" type="xsd:string"/>
<xsd:element name="lastName" type="xsd:string"/>
<xsd:element name="ageInYears" type="xsd:int"/>
<xsd:element name="weightInLbs" type="xsd:float"/>
<xsd:element name="heightInInches" type="xsd:float"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ArrayOfPERSON">
<xsd:complexContent>
<xsd:restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType"
wsdl:arrayType="typens:PERSON[]"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
</schema>
</types>
<message name="addPersons">
<part name="person" type="typens:ArrayOfPERSON"/>
</message>
<message name="addPersonResponse">
<part name="result" type="xsd:int"/>
</message>
</definitions>
<portType> 與 <operation> 元素
PortType 可在抽象中,定義多種作業(yè)。PortType 中的作業(yè)元素,可定義呼叫所有 PortType 方法的語法。每個作業(yè)元素都會宣告,方法的名稱、參數(shù) (使用 <message> 元素)、類型 (每個 <message> 中所宣告的 <part> 元素)。
在 WSDL 文件中,可有多個 <portType> 元素。每個 <portType> 元素,群組化多個相關(guān)作業(yè)的方式,與 COM 介面群組化方法的方式非常類似。
在一個 <operation> 元素中,最多可有一個 <input> 元素、一個 <output> 元素、與一個 <fault> 元素。這三個元素都各有一個名稱與訊息屬性。
在 <input>、<output>、與 <fault> 元素中使用名稱屬性的目的為何?原來是為了,區(qū)別具相同名稱 (多載) 的兩項作業(yè)。例如,下列兩個 C 函數(shù),即具有相同的名稱,但不同的參數(shù)。
void foo(int arg);
void foo(string arg);
使用 WSDL 時,這種多載的表達(dá)方式如下:
<?xml version="1.0" encoding="UTF-8" ?>
<definitions name="fooDescription"
targetNamespace="http://tempuri.org/wsdl/"
xmlns:wsdlns="http://tempuri.org/wsdl/"
xmlns:typens="http://tempuri.org/xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:stk="http://schemas.microsoft.com/soap-toolkit/wsdl-
extension"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<types>
<schema targetNamespace="http://tempuri.org/xsd"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
elementFormDefault="qualified" >
</schema>
</types>
<message name="foo1">
<part name="arg" type="xsd:int"/>
</message>
<message name="foo2">
<part name="arg" type="xsd:string"/>
</message>
<portType name="fooSamplePortType">
<operation name="foo" parameterOrder="arg " >
<input name="foo1" message="wsdlns:foo1"/>
</operation>
<operation name="foo" parameterOrder="arg " >
<input name="foo2" message="wsdlns:foo2"/>
</operation>
</portType>
<binding name="fooSampleBinding" type="wsdlns:fooSamplePortType">
<stk:binding preferredEncoding="UTF-8" />
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="foo">
<soap:operation soapAction="http://tempuri.org/action/foo1"/>
<input name="foo1">
<soap:body use="encoded" namespace="http://tempuri.org/message/"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
</input>
</operation>
<operation name="foo">
<soap:operation soapAction="http://tempuri.org/action/foo2"/>
<input name="foo2">
<soap:body use="encoded"
namespace="http://tempuri.org/message/"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
/>
</input>
</operation>
</binding>
<service name="FOOService">
<port name="fooSamplePort" binding="fooSampleBinding">
<soap:address
location="http://carlos:8080/fooService/foo.asp"/>
</port>
</service>
</definitions>