大多數情況下,JAX-RPC 假定出現在 SOAP 消息中的 XML 元素都應該映射到 Java 對象中去。對於簡單類型和復雜類型如何映射各有其規則說明,JAX-RPC 實現通常都提供了生成處理映射所需代碼的工具。好消息就是,如果您要處理應用程序中的 Java 對象,您不必擔心數據的類型映射以及序列化和反序列化。也就是說,您不需要知道如何解析 XML 元素,如何將它交給適當的 Java 對象,反之亦然。然而,在某些情況下,您可能想掌握(反)序列化是如何實現的。或者您根本就不想將 XML 數據映射到 Java 對象中去。幸運的是,在 JAX-RPC 中就有辦法能做到,本文將為您展示如何去做的技巧。
SOAP with Attachments API for Java(SAAJ)
JAX-RPC 規范依賴於 SAAJ 來處理 SOAP 消息的。SAAJ 定義了 SOAP 消息中各個部分的類與接口。首先介紹 javax.xml.soap.SOAPMessage 類,您可以通過這個類訪問 javax.xml.soap.SOAPEnvelope 實例,緊接著又可以通過這個實例獲取 javax.xml.soap.SOAPHeader 實例和 javax.XML.soap.SOAPBody 實例,等等。所有這些實例都是對 Javax.XML.soap.SOAPElement 接口的擴展與繼承,而這個接口本身也是對 W3C DOM Node 接口的擴展。
換句話說,SAAJ 給您提供了以 XML 為中心的角度來看待 SOAP 消息中的元素。JAX-RPC 將所有的消息處理都委托給了 SAAJ。請注意此處我並不打算全面地介紹 SAAJ,我只想討論一些本文主題所需要的方面。
<xsd:any/> 元素
<xsd:any/> 元素是表示 XML 文檔中任意 XML 內容的元素。顧名思義,它可以是任何一種 XML。這使您可以在 XML Schema 中創建復雜類型定義,而不用描述復雜類型的某些部分的具體結構。下面是一個示例(請參見 清單 1)展示了一個名為 Order 類型的定義。它包含兩個普通元素和一個 <xsd:any/> 元素。
清單 1. 帶有 <xsd:any/> 的 XML Schema
<schema elementFormDefault="qualifIEd"
targetNamespace="http://anytip.webservices.ibm.com"
xmlns="http://www.w3.org/2001/XMLSchema">
<complexType name="Order">
<sequence>
<element name="createDate" nillable="true" type="xsd:dateTime"/>
<element name="customer" nillable="true" type="xsd:string"/>
<xsd:any maxOccurs="unbounded"/>
</sequence>
</complexType>
</schema>
這種類型的實例可以包含任意多個附加 XML 元素同時卻不違反 schema 定義。這樣一來,您就可以將附加信息添加到 Order 元素,而不用在 Schema 中定義它的格式。
組合在一起
那麼,JAX-RPC 如何處理包含 <xsd:any/> 元素的 XML Schema 呢?最新發布的 JAX-RPC 1.1規范中定義了這種元素被映射到 SAAJ 的 javax.xml.soap.SOAPElement 接口(請參閱 參考資料)。這意味著服務端點接口將會包含有一個參數或者在 schema 中用到 <xsd:any/> 的每個地方返回 javax.XML.soap.SOAPElement 類型的值,如果 maxOccurs 屬性值大於1,就分別返回 Javax.XML.soap.SOAPElement[] 類型的值。
因此,JAX-RPC 工具將從上面的樣本 Schema 中生成以下類(請參見 清單 2)(與通常的情況一樣,這個樣本沒有什麼意義,僅僅只是說明了概念;如常,您也可以在 參考資料部分找到本文中的完整源代碼):
清單 2. 生成的 Order 類
public class Order implements Java.io.Serializable {
private Java.util.Calendar createDate;
private Java.lang.String customer;
private Javax.XML.soap.SOAPElement[] _any;
...
}
所有這些與自定義序列化有什麼關系呢?假定您的 Web 服務使用了一些您並不想映射到 Java 類中的數據,而是想讓 JAX-RPC 引擎以 XML 形式將這些數據交給實現。該實現然後就就能夠解析數據或者簡單地將它作為 XML 傳遞到後端應用程序以進行進一步的處理。類似地,您可以創建一個客戶端代理,這個代理允許您在 SOAPElement 而不是 Java 對象中傳送。
示例
這個示例在您預先知道 XML 數據結構的情況下也能夠正常運行。設想您使用的是一個 OrderManager Web 服務,它是基於某些搜索標准來返回 Order 數據的。每個 Order 實例都包含有很多行式項目(line items)。 清單 3 中是對 WSDL 的摘錄。假定這是一個非常簡單易懂的 Web 服務,我們將只關注此處的 <types> 部分。
清單 3. 用於 Web 服務的 WSDL
<?XML version="1.0" encoding="UTF-8"?>
<wsdl:definitions ...>
<wsdl:types>
<schema elementFormDefault="qualifIEd"
targetNamespace="http://any.webservices.ibm.com"
xmlns="http://www.w3.org/2001/XMLSchema">
<element name="getOrder">
<complexType>
<sequence>
<element name="searchCriteria"
nillable="true" type="xsd:string"/>
</sequence>
</complexType>
</element>
<complexType name="Order">
<sequence>
<element name="createDate" nillable="true" type="xsd:dateTime"/>
<element name="customer" nillable="true" type="xsd:string"/>
<element name="lineItems" nillable="true" type="impl:LineItem"
maxOccurs="unbounded"/>
</sequence>
</complexType>
<complexType name="LineItem">
<sequence>
<element name="itemDesc" nillable="true" type="xsd:string"/>
<element name="itemNumber" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<element name="getOrderResponse">
<complexType>
<sequence>
<element name="getOrderReturn"
nillable="true" type="impl:Order"/>
</sequence>
</complexType>
</element>
</schema>
</wsdl:types>
...
</definitions>
現在想象一下您不想讓 JAX-RPC 引擎來生成與結構相映射的 LineItem Java 類(如 XML Schema 中所定義的),而想自己來處理 XML (或許因為您要將它直接作為 XML 存儲在數據庫中,以致於解析數據就顯得毫無必要了)。因此,在此 Web 服務中生成客戶端代碼之前,您要更改 WSDL 文件,⒁? <xsd:any/> 元素替換掉表示 lineItem 的元素。這樣現在的 Order 復雜類型看起來就跟您之前看到的完全一樣了。JAX-RPC 工具現在將生成一個包含有 SOAPElement 屬性的客戶端 Order 類,而不是 lineItem 類。上面的示例已經展示該類。
現在,您可以創建將從 Web 服務檢索 Order 的客戶端代碼,並以 XML 的格式使用行式項目信息。 清單 4 中是一個示例測試客戶端,這個示例是使用 WebSphere Application Server V5.0.2 生成的,您可以看到它可以看起來是什麼樣的:
清單 4. 樣本客戶端代碼
public class TestMain {
public static void main(String[] args) throws Exception {
OrderManager om =
new OrderManagerServiceLocator().getOrderManager();
Order order = om.getOrder("whatever");
SOAPElement lineItem = order.get_any()[0];
System.out.println("Line item is : "+lineItem.toString());
}
}
此客戶端應用程序將檢索作為 Java 對象的 Order ,但行式項目信息是以 XML 的格式作為 SOAPElement 的一個數組返回的。
總結
JAX-RPC 提供了以 Java 為中心的角度來看待 Web 服務。如果您想自己處理 SOAP 消息中的某些 XML 結果,您可以使用 SAAJ API 並結合 <xsd:any/> 元素來實現。您可以期待著未來版本的 JAX-RPC 引擎實現將會使這一切更加容易。事實上,JAX-RPC 1.1 規范要求符合規范的工具對每種類型都提供是映射到 Java 類還是映射到 Javax.XML.soap.SOAPElement 的選項。