例析JAX-RPC 1.1中的Java/XML数据类型映射

出处:http://www.ibm.com/developerworks/cn/webservices/ws-jxmap/
本文将讨论JAX-RPC1.1中JAVA编程语言的数据类型与XML Schema数据类型之间的映射,并且提供具体的映射实例。

本文是J2EE Web服务开发系列文章的第十篇,本文将讨论JAX-RPC1.1中JAVA编程语言的数据类型与XML Schema数据类型之间的映射,并且提供具体的映射实例。通过学习本文,读者能够理解JAX-RPC中Java到XML之间的映射关系,并且能够在开发 中合理处理不同的数据类型。

阅读本文前您需要以下的知识和工具:

  • J2EE1.4 SDK,并且会初步使用;
  • 掌握基本的JAX-RPC Web服务开发技能;
  • 一般的Java编程知识。

本文的参考资料见 参考资料

本文的全部代码在 这里 下载。

为什么要映射

 

由 于Web服务调用是通过XML进行数据交换的远程方法调用,它不像本地调用可以通过引用传递参数,JAX-RPC目前的版本只支持按值传递参数。也就是 说,当要通过远程方法调用Web服务时,把参数的一个拷贝传递过去。默认情况下,非静态的和非transient的变量才会拷贝。当从Web服务端返回调 用结果时,又产生一个新的对象。

鉴于Web服务以上的调用机制,所以在参数传递时有一定的限制,它只支持以下的数据类型:

  • 原始类型(任何Java支持的数据类型,如int);
  • 标准java类(String等);
  • 由支持的Java类型组成的数组(Array);
  • 异常类;
  • JAX-RPC值类型。

注意:只有JAX-RPC支持的数据类型才能在Web服务中作为参数传递和返回。

我们知道,SOAP、WSDL是和编程语言无关的Web服务协议,故SOAP、WSDL并不定义XML和编程语言之间的映射。JAX-RPC1.1规范定义了以下JAVA到XML之间的映射关系:

  • 简单的内置(built-in)类型的映射(如:xsd:string--java.lang.String);
  • 数组(Array,如int[] ,String[][]等),映射到 Java array;
  • 简单的内置(built-in)类型的枚举(Enumeration), 映射到 Java Enumeration;
  • XML Struct 和复杂类型,映射到JavaBean,这个Bean有相关的 getter 和 setter方法。

下面详细讨论这些数据类型的映射。

 




回页首


原始类型

 

下表是Java原始数据类型和XML数据类型之间的映射

表1 原始数据类型与XML 数据类型之间的映射

Java原始数据类型 XML 数据类型
boolean xsd:boolean
byte xsd:byte
short xsd:short
int xsd:int
long xsd:long
float xsd:float
double xsd:double

注意:这里的原始数据类型中没有char类型。

我们知道,在Java语言中,每个原始数据类型都有一个对应的Object类,如int和Integer对应,但事实上如果它们表示同一个整数时对于XML来说是一样的,所以XML把它们统一对待,如:

例程1 Integer的表示方法

<xsd:element name="code" type="xsd:int" nillable="true"/>
<!-- Schema instance -->
<code xsi:nil="true"></code>

 

这样,我们可以在Web服务编程中按照以下的方式来使用原始数据类型:

例程2 原始数据类型的使用

          …//Web服务端点接口中的代码。
 public int echoint(int original)throws java.rmi.RemoteException;
…//Web服务实现类中的代码。
 public int echoint(int original){  return original; }
…//Web服务客户端代码。
System.out.print("echoint:");
     System.out.println(demo.echoint(100));
     





回页首


标准java类

 

标准Java类和XML 数据类型之间的映射关系如表2所示。

表2 标准Java类和XML 数据类型之间的映射

Java Class XML 数据类型
java.lang.String xsd:string
java.math.BigInteger xsd:integer
java.math.BigDecimal xsd:decimal
java.util.Calendar xsd:dateTime
java.util.Date xsd:dateTime
javax.xml.namespace.QName xsd:QName
java.net.URI xsd:anyURI

java.util.Calendar和java.util.Date都与XML的xsd:dateTime对应。

这样,我们可以在Web服务编程中按照以下的方式来使用这些标准Java类型:

例程3 Java标准类的使用

     …//Web服务端点接口中的代码。
 public String echoString(String original)throws java.rmi.RemoteException;
…//Web服务实现类中的代码。
public String echoString(String original){  return original; }
…//Web服务客户端代码。
System.out.print("echoString:");
System.out.println(demo.echoString("Hello"));

 

可以看出,和原始数据类型一样,我们并不需要关心JAX-RPC客户端和服务端怎么进行类型转换,而是由JAX-RPC运行环境进行自动转换。

 




回页首


由支持的Java类型组成的数组(Array)

 

JAX-RPC支持由JAX-RPC支持的数据类型组成的数组。比如int[],String[]等。多维数组同样可以被支持,比如:int[][],String[][]。

另外,byte[] 和 Byte[] 都映射为xsd:base64Binary 类型。

例如,可以按照以下方式来使用数组:

例程4 数组的使用

            …//Web服务端点接口中的代码。
public int[] echoints(int[] original)throws java.rmi.RemoteException;
public String[] echoStrings(String[] original)throws java.rmi.RemoteException;
public String[][] echoString2s(String[][] original)throws java.rmi.RemoteException;
public MyPoint[] echoMyPoints(MyPoint[] original)
throws java.rmi.RemoteException,InvalidPointException;
…//Web服务实现类中的代码。
public String[][] echoString2s(String[][] original){  return original; }
…//Web服务客户端代码。
///////////原始类型的数组///////////
            byte[] ret=demo.echobytes(new byte[]{1,1});
            for(int i=0;i<ret.length;i++)
            {
             System.out.println(ret[i]);
            }
///////////基本类型的数组///////////        
            System.out.print("echoString[]:");
            String[] strings=new String[]{"test1","test2"};
            String[] ret_strings=demo.echoStrings(strings);
            for(int i=0;i<ret_strings.length;i++)
            {
             System.out.println(ret_strings[i]);
            }
            

 

注意,上面的代码中包含了一维的数组,也包含了二维的数组。数组中的数据类型也各不相同。我们看String[]和String[][]怎么在WSDL中描述的:

例程5 String[]和String[][]到XML的映射

  <!--String[][]到XML之间的映射--> 
<complexType name="
        ArrayOfArrayOfstring">
 <complexContent>
 <restriction base="soap11enc:Array">
  <attribute ref="soap11enc:arrayType" wsdl:arrayType="
        tns:ArrayOfstring[]" /> 
  </restriction>
  </complexContent>
  </complexType>
<!--String[]到XML之间的映射--> 
 <complexType name="
        ArrayOfstring">
 <complexContent>
 <restriction base="soap11enc:Array">
  <attribute ref="soap11enc:arrayType" wsdl:arrayType="
        string[]" /> 
  </restriction>
  </complexContent>
  </complexType>
  
      

 

string[] 是XML Schema定义的标准数据类型。在上面映射关系中,Java中String[]映射成Schema一个复杂数据类型:ArrayOfstring。而 String[][]以String[]为基础进一步定义,它的arrayType是tns:ArrayOfstring[]。

 




回页首


异常类

 

JAX-RPC规定Web服务抛出的一样类必须直接或者间接从java.lang.Exception类继承,这个异常必须是可检查的异常(checked Exception),必须不能直接或者间接继承自java.lang. RuntimeException类。

如我们可以定义如下的异常类:

例程6 定义JAX-RPC异常类

package com.hellking.study.webservice.datatype;
/**
 *jax-rpc异常类。
 */
public class InvalidPointException 
        extends java.lang.Exception
{
 public InvalidPointException(String msg)
 {
  super(msg);
 }
 public String toString()
 {
  return "InvalidPointException"+getMessage();
 }
}
      

 

可以在JAX-RPC端点接口和实现类中按以下方式来使用这个异常:

例程7 使用异常

          …//Web服务端点接口中的代码。
 public MyPoint echoMyPoint(MyPoint original)
throws java.rmi.RemoteException,InvalidPointException;
 
…//Web服务实现类中的代码。
 public MyPoint echoMyPoint(MyPoint original)
throws InvalidPointException{  return original; }
…//在客户端使用异常
try
            {
             
             MyPoint ret_mp=demo.echoMyPoint(mp);
             System.out.println(ret_mp.toString());
          }
          catch(InvalidPointException e)
          {
           e.printStackTrace();
          }
          

 

这样,当在服务端实现类出现异常时,将通过SOAP消息把异常类发送到客户端。我们看这个异常类怎么和XML数据类型映射。

例程8 异常和XML数据类型映射

  <complexType name="InvalidPointException">
 <sequence>
  <element name="message" type="string" nillable="true" /> 
  </sequence>
  </complexType>
  

 

可以看出,异常类实际上通过string类型来发送的,其实发送的string就是异常的错误堆栈,在客户端获得XML形式的错误堆栈后,由可以通过这个错误堆栈重新构造一个新的异常类,它和服务端抛出的异常类是等价的。

 




回页首


JAX-RPC值类型

 

JAX-RPC值类型是能够在客户端和服务端点之间传递的Java类,它被映射为xsd:complexType。JAX-RPC值类型必须遵守以下规则:

  • Java类必须提供默认的构造方法;
  • Java类必须不(直接或者间接)实现java.rmi.Remote接口;
  • 可以实现任何Java接口(除java.rmi.Remote接口外)或者扩展自其它类;
  • 它可以包含公共的、私有的、受保护的字段,并且公共的字段必须是JAX-RPC支持的数据类型;
  • Java类可以包含静态或者transient字段;
  • Java类可以包含一些方法;
  • Java类可能被设计成JavaBean。在这种情况下,JavaBean必须依照JavaBeans组件规则,为它的属性提供getter和setter方法;并且JavaBean的属性必须是JAX-RPC支持的数据类型。

例如,可以定义一个如下的值类型:

例程9 JAX-RPC的值类型

package com.hellking.study.webservice.datatype;
/**
 *jax-rpc值类型:Value Type
 */
public class MyPoint
{
 int x;
 int y;
 public MyPoint(){}
 public int getX(){return x;}
 public int getY(){return y;}
 public void setY(int y){this.y=y;}
 public void setX(int x){this.x=x;}
 
 public String toString()
 {
  return new StringBuffer().append("X:").append(x).append("Y:").append(y).toString();
 }
}

 

注意,MyPoint的构造方法必须是默认的:public MyPoint(){}。我们为这个值类型提供了对应的getter和setter方法,它可以说是一个JavaBean。

其它的值类型可以从这个值类型扩展,如例程10所示。

例程10 值类型的继承

package com.hellking.study.webservice.datatype;
/**
 *jax-rpc值类型:Value Type
 */
public class ExtendPoint extends MyPoint
{
 int z;
 
 public ExtendPoint(){}
 public int getZ(){return z;}
 public void setZ(int z){this.z=z;}
 
 public String toString()
 {
  return new StringBuffer(super.toString()).append("Z:").append(z).toString();
 }
}

 

实际上,对于上面的值类型,XML仍然作为复杂数据类型来处理的,我们来看它的定义:

例程11 值类型和XML之间的映射

  <complexType name="
        ExtendPoint">
 <complexContent>
 
        <extension base="tns:MyPoint">
 <sequence>
  <element name="z" type="int" /> 
  </sequence>
  </extension>
  </complexContent>
  </complexType>
 <complexType name="
        MyPoint">
 <sequence>
  
        <element name="x" type="int" /> 
  <element name="y" type="int" /> 
  </sequence>
  </complexType>
  
      

 

可以看到,Java值类型的扩展(extend)对应于XML Schema中定义的扩展(extension)。

 




回页首


类型映射的扩展

 

JAX- RPC规范规定了XML数据类型和Java类型之间的标准的映射,这些映射支持SOAP1.1编码和XML Schema规则。除了这些标准的映射之外,JAX-RPC也提供了一些扩展映射的API,这些API在javax.xml.rpc.encoding 包中定义,通过这些API,可以开发出可插入的序列化和反序列化器。

注意: XML/SOAP序列化语义代与标准的Java序列化不同。标准的序列化状态包含public、private、protected以及包级的非 transient字段。RMI-IIOP使用标准的序列化语义来序列化值类型。而XML/SOAP序列化通常把值类型表示成对应的XML形式。

序列化类型扩展的实现是和具体的厂商相关的,所以在这里不准备详细讨论。在J2EESDK1.4中,支持以下类型的序列化:

  • List(ArrayList、LinkedList、Stack、Vector);
  • Map(HashMap、HashTable、Properties、TreeMap);
  • Set(HashSet、TreeSet)。

注意:J2EESDK1.4实现不支持Collection类的序列化。

这样,我们可以在编程中按照以下的方式来使用Java集合类型:

例程12 使用扩展类型映射

     …//Web服务端点接口中的代码。
/**
     *集合类
     */
     public Map echoMap(Map original)throws java.rmi.RemoteException;
     public List echoList(List original)throws java.rmi.RemoteException;
     public Set echoSet(Set original)throws java.rmi.RemoteException;
…//Web服务实现类中的代码。
 public Map echoMap(Map original){ return original; }
     public List echoList(List original){ return original; }
     public Set echoSet(Set original){ return original; }
     

 

我们可以看到在生成的WSDL文件中,JAX-RPC参考实现工具进行了以下方式的映射:

例程13 扩展类型映射

  <schema 
        targetNamespace="http://java.sun.com/jax-rpc-ri/internal"
 xmlns:tns="http://java.sun.com/jax-rpc-ri/internal" 
xmlns:soap11-enc="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://www.w3.org/2001/XMLSchema">
  <import namespace="http://schemas.xmlsoap.org/soap/encoding/" /> 
  <import namespace="urn:dataType" /> 
<complexType name="
        list">
<complexContent>
<extension base="tns:
        collection">
  <sequence /> 
  </extension>
  </complexContent>
  </complexType>
<complexType name="
        collection">
<complexContent>
<restriction base="soap11-enc:Array">
  <attribute ref="soap11-enc:arrayType" wsdl:arrayType="anyType[]" /> 
  </restriction>
  </complexContent>
  </complexType>
<complexType name="
        map">
<complexContent>
<restriction base="soap11-enc:Array">
  <attribute ref="soap11-enc:arrayType" wsdl:arrayType="tns:mapEntry[]" /> 
  </restriction>
  </complexContent>
  </complexType>
<complexType name="
        mapEntry">
<sequence>
  
        <element name="key" type="anyType" /> 
  <element name="value" type="anyType" /> 
  </sequence>
  </complexType>
<complexType name="
        set">
<complexContent>
<extension base="tns:
        collection">
  <sequence /> 
  </extension>
  </complexContent>
  </complexType>
  </schema>
  
      

 

首 先,从名称空间targetNamespace="http://java.sun.com/jax-rpc-ri/internal"可以看出上面的映 射是和JAX-RPC参考实现相关的。List和Set都以collection为基础定义,而collection则以XML 的Array为基础定义的。而Map的定义基于mapEntry,mapEntry就是包含了key和value元素的复杂XML数据类型。

需要注意的是,List、Map或者Set集合对象中包含的数据类型必须是JAX-RPC支持的数据类型,否则序列化将失败。

 




回页首


运行示例程序

 

本文后面附带了上面例子中的代码。首先在J2EE1.4SDK下部署这个应用(java-xml.ear),然后转到src目录,通过执行:

ant run-client
来运行客户端程序。
运行的效果如图1所示。


图1 运行示例程序




回页首


总结

 

通 过上面的介绍,我们应该能够理解JAX-RPC数据类型和XML数据类型之间的映射关系,并且能够适当使用JAX-RPC数据类型。需要注意的是,只有 JAX-RPC支持的数据类型才能在Web服务中作为参数传递和返回。JAX-RPC除了提供了标准的映射外,还提供了扩展的数据类型映射框架,通过这个 框架,不同的厂商或者开发者可以根据自己的需要开发自定义的序列化和反序列化器,从而实现复杂数据类型的映射。比如开发者为了提高系统性能,开发出基于 SAX的序列化器。


参考资料

本文的全部代码在 这里 下载。

下载J2EESDK1.4 http://java.sun.com/downloads/

J2EE Home Page, java.sun.com/j2ee

J2EE 1.4 (JSR 151), www.jcp.org/jsr/detail/151.jsp

Web Services for J2EE (JSR 109), www.jcp.org/jsr/detail/109.jsp

JAXM API http://java.sun.com/webservices

Jwdp1.3 http://java.sun.com/webservices

JAX-RPC规范, http://java.sun.com/xml/downloads/jaxrpc.html