级别: 初级 Andre Tost (atost@us.ibm.com), 高级认证 IT 专家, IBM Software Group
2004 年 2 月 01 日 大多数情况下,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 的选项。
参考资料
关于作者  | |  | Andre Tost 是 WebSphere Business Development 组的一位方案设计师,在那里他帮助 IBM 的策略联盟(IBM Strategic Alliance )合作伙伴使用
WebSphere 集成他们的应用程序。他主要研究整个 WebSphere 产品家族中的 Web 服务技术。在担任现在职务之前,他在各类开发和 IBM
软件开发中担任架构师角色已经有 10 年了,最近主要研究 WebSphere Business Components 产品。他来自德国,现在居住在 Rochester
,Minnesota,并在那里工作。在空闲时间,他喜欢与家人在一起或者有可能就看看足球。您可以通过
atost@us.ibm.com
与 Andre 联系。
|
对本文的评价
|