使用 xsd:any 表示服务接口中的 XML 文档

作者:Sean BrydonSmitha KangathSameer Tyagi

问题描述

开发者在设计 Web 服务交互环境(客户端和服务在其中交换 XML 文档)时,需要选择一种表示服务接口中的 XML 文档的类型。类型的选择会带来多方面的影响。选定的类型将在 WSDL 服务描述文件和服务实现代码中反映出来。在 Java[TM] 2 Platform, Enterprise Edition(J2EE[TM] 平台)1.4 应用程序中,XML 文档(如订单或发票)是使用基于 XML 的远程过程调用的 Java API (JAX-RPC) 交换的。在设计服务接口时,选择表示 XML 文档的类型时有许多可供考虑的策略:
选择使用 xsd:any 表示面向文档的服务中的 XML 文档时,此解决方案重点考虑的是设计因素。有关所有这些策略的描述,请参见面向文档的服务项。

解决方案

让我们考虑一些在将 XML 片段与 xsd:any 一起使用表示客户端和服务之间交换的 XML 文档时出现的问题。使用 xsd:any 表示服务接口中交换的 XML 文档的解决方案在出现以下情况时非常有用:服务端点可以接收多种类型的文档,而每个文档都具有自己的结构。并且,在您希望将接口设计为接收其中任一类型的文档时,这会有助于您设计代理或更为通用的服务接口。由于 xsd:any 类型在本质上是通配符,因此在您希望接收同一操作的具有不同结构的文档时,可以使用该类型。由于 WSDL 接口会将 xsd:any 指定为要接收的 XML 文档的类型,因此 WSDL 不包含服务可能需要的各种文档结构。因此,服务的客户端必须找到另外一种机制(如辅助文档),才能确定所需的一组文档类型。服务实现需要编写代码以验证和处理所需的一组文档,同时处理出现意外结构的文档。这与 WSDL 文件为服务使用的文档指定使用结构定义的类型的策略相反。简而言之,当您需要通用操作,其中服务操作使用一组不同的 XML 文档时,使用 xsd:any 的解决方案是很有用的,但是这会给处理那些类型的服务实现者带来负担(诸如验证、处理、绑定到 Java 对象等方面)。

策略:将 XML 片段与 xsd:any 一起使用来表示 XML 文档

在 XML 结构中,通配符 <xsd:any> 元素允许使用该结构未指定的元素扩展复杂类型。当复杂类型的内容确实不需要在结构中定义时,使用 <xsd:any> 元素也会非常有用。例如,请考虑以下情况:服务操作可能会接收多种类型的订单 XML 文档,并且每个订单都定义了不同的结构。在这种情况下,将元素订单文档类型描述为服务接口中的 <any> 是很有用的。这样会使端点更为通用。这还意味着服务实现代码必须确定它接收的 xml 文档的类型,并对其进行正确处理。使用 <any> 会使服务操作更为通用,因此您的代码必须能够处理它。在代码示例 1 中的 WSDL 文件中,请注意有一个包括 <any> 元素的已定义类型 submitPO,它在一定程度上将 <any> 封装在了其他元素声明中。然后在服务接口中使用此 submitPO 类型作为提交订单的操作(请注意,元素名称 submitPO 和操作名称 submitPO 是相同的,这只是因为 WSDL 使用了指定这些名称匹配约定的封装样式)。在此服务中,所接收的文档可以在 XML 中具有任何元素。

<types>
  <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="urn:AnyPurchaseOrderService">

    <complexType name="submitPO">
      <sequence>
          <any/>                       
      </sequence>
    </complexType>   
    <element name="submitPO" type="tns:submitPO"/>
  </schema>
</types>

<message name="AnyPurchaseOrderServiceSEI_submitPO">
  <part name="parameters" element="tns:submitPO"/>
</message>
 
...

<portType name="AnyPurchaseOrderServiceSEI">
  <operation name="submitPO">
      <input message="tns:AnyPurchaseOrderServiceSEI_submitPO"/>
      ...
  </operation>
</portType>

代码示例 1:显示将 <any> 用作服务操作输入参数的 WSDL 代码片段

在 JAX-RPC 中,当使用 xsd:any 结构类型元素表示元素通配符时,复杂类型的映射将映射到 JavaBeans[TM] 组件。如果 <any> 元素在 maxOccurs 等于 1 时,则会出现一个名为 _any 的附加属性映射到 javax.xml.soap.SOAPElement(如果 maxOccurs 大于 1,则它会映射到 javax.xml.soap.SOAPElement 的数组)。此外,在 JAX-RPC 中,如果 XML 结构类型没有标准的 Java 映射,则会考虑具有类型表示的消息部分并将它映射为 XML 文档片段。XML 到 Java 的映射使用接口 javax.xml.soap.SOAPElement 来表示 wsdl:operation 元素的 Java 映射中的类型消息部分。因此,如果从 WSDL 文件(如代码示例 1 所示,将 <any> 作为被接收的文档的类型)生成 Java 接口,则 Java 接口会具有 SubmitPO 类型的封装类,如代码示例 2 所示。在这种情况下,此接口的 Java 实现将需要为 SubmitPO 参数调用 getter 方法 get_any(),以获取封装在内部的 SOAPElement。使用这一额外步骤进行调用会产生一些不方便,但不会带来很大的麻烦。

public interface AnyPurchaseOrderServiceSEI extends Remote {
    public SubmitPOResponse submitPO(SubmitPO parameters) throws
        InvalidPOException,  java.rmi.RemoteException;
}

代码示例 2:访问 Web 服务的服务接口

参考资料

有关本主题的详细信息,请参阅以下资料:

© Sun Microsystems 2005。Java BluePrints Solutions Catalog 中的所有内容受版权保护,未经 Sun Microsystems 的明确书面许可,不得在其他产品中发布。