让Axis支持EMF模型

Axis Java 阵营中最常用的一个 Web 服务组件。通过一些配置就可以利用它去生成、部署 Web 服务。


但是目前
Axis 只支持 XMLBean Castor JavaBean 复杂类型数据结构,于是在使用的时候,特别是一些比较高级的 Web 服务使用的时候,复杂数据类型就会受到一定的限制。这里我给出一个关于如何让 Axis 支持 EMF 模型的例子,希望能借此能给读者一些提示。在这里我假设读者都使用过 Axis ,如果需要获得更多的 Axis 信息,请点这里。关于EMF的信息可以从这里获得。相关代码下载


1.      
类型映射

Axis Server-config.wsdd 文件中,我们需要自己定义部署的服务的一些配置信息,其中有一个名为 typeMapping 的元素,该元素就是配置如何映射复杂类型数据结构的一些信息。 typeMapping 元素具有以下属性:


1) 
Deserializer  反序列化 XML 到我们所需要对象的 DeserializerFactory

   2)  Serializer     序列化对象到 XML SerializerFactory

   3)  encodingStyle  编码类型,我一般设为空

   4)  type            需要映射的复杂类型数据对象类

   5)  qname           数据类型在 XML 中对应的 QName (命名空间加上元素的名称)

 

当我们访问某个 Web 服务时,如果返回类型,或者是我们的客户端调用代码中涉及到了复杂类型, Axis 会去查询我们 typeMapping 中定义好的并且和该复杂类型匹配的 DeserializerFactory 或者 SerializerFactory 类,然后返回对应的 Deserializer Serializer 类,通过该类序列化或者反序列化这个复杂类型。所以如果我们所定义的复杂类型并非 Castor JavaBean XMLBean 的时候,就需要我们自己去创建 DeserializerFactory SerializerFactory


2
DeserializerFactory Deserializer

反序列化工厂是用户将 XML 转成 Java 对象的工厂,它维护了一个 Deserializer 对象,该对象才是真正去做反序列化工作的类。

我们现在创建自己的 Deserializer 类以及 Deserializer 工厂类:

public   class  EMFDeserializerFactory  extends  BeanDeserializerFactory {

  
public  EMFDeserializerFactory (Class javaType, QName xmlType) {

         
super (javaType, xmlType);

         deserClass 
=   EMFDeserializer. class ;

  }

 
public  Deserializer getDeserializerAs(String mechanismType)  throws  JAXRPCException {

         
return   new  EMFDeserializer(javaType,xmlType);

 }
}

 

工厂类的实现比较简单,我们先继承 BeanDeserializerFactory 类,然后复写它的 getDeserializerAs 方法,返回相应的 Deserializer 类即可。

 

public   class  EMFDeserializer  extends  DeserializerImpl {

       
private  Object sdoValue  =   null ;

       
private  Class valueVlazz  =   null ;

       
public  EMFDeserializer(Class arg0, QName arg1) {

              valueVlazz 
=  arg0;

       }

       
public   void  onEndElement(String namespace, String localName,

                     DeserializationContext context) 
throws  SAXException {
           .
       }
 }


EMFDeserialize
的工作就是将 XML 文档转成 EMF 模型。

我们继承 DeserializeImpl 类,然后复写它的 onEndElement 方法。


public   class  EMFDeserializer  extends  DeserializerImpl {

       
public   void  onEndElement(String namespace, String localName,

                     DeserializationContext context) 
throws  SAXException {

              …….

}

}



onEndElement 方法中的参数有一个 DeserializationContext 对象,我们可以通过它获得 XML 文档片段:


Document doc  =  context.getCurElement().getAsDocument();



Document 就是得到的 XML 片段。


然后我们通过
EMF 提供的 Resource 对象将 XML 文档进行反序列化(这里我自己写了一个创建 XMLResourceImpl 的工厂类,读者可以在文章后面下载代码):


org.apache.xml.serialize.DOMSerializerImpl s1  =  
     new  org.apache.xml.serialize.DOMSerializerImpl();
String ddd 
=  s1.writeToString(doc);

Resource resource 
=  GenaralEObjectXMLResourceFactory.getInstance()

                                   .createResource(
null );

resource.load(
new  ByteArrayInputStream(ddd.getBytes()),

                                   Collections.EMPTY_MAP);



先将
Document 转成 String ,然后通过 ByteArrayInputStream 传给 resource,resource load 后会生成对应的数据对象,并且我们可以通过 resource 获得:


sdoValue  =  resource.getContents().get( 0 );

value 
=  sdoValue;



这里需要注意的是,如果我们生成的
EMF 模型是通过 XSD 来生成的,那反序列化后得到的应该是一个 DocumentRoot 对象,该对象可能并不是我们想得到的,比如:


<? xml version=”” encoding=”UTF-8” ?>

< student  name =”me”/>



得到的对象并是不
Student 对象,而是一个 DocumentRoot ,而 Student 对象是包含在 DocuementRoot 对象的子对象中。

但是如果是直接通过 Ecore 模型生成的则不会有 DocumentRoot 对象。这里如何去判断得到的对象是不是 DocumentRoot 需要读者进一步去看看 EMF ,这里我没有给出通用的手段解决,只是根据我本身程序的需求去获得的:


if (sdoValue  instanceof  EObject){
    List contents 
=  ((EObject)sdoValue).eContents();
    
for  (Iterator iter  =  contents.iterator(); iter  .hasNext();) {

             Object element 
=  (Object) iter.next();

           
if (valueVlazz.isInstance(element)){

                      value 
=  element;

                      
break ;

            }

    }

 }



这里需要注意,对象
value 并不是 EMFDeserializer 自己定义的字段,而是 DeserializerImpl 的字段,该字段就是 Axis 反序列化后的对象值引用字段。


3.   
Serializer SerializerFactory


SerializerFactory
的工作和 DeserializerFactory 工作类似,返回一个 Serializer 对象,它是真正去将对象序列化成 XML 的类。

SerializerFactory 比较简单,这里就不详细介绍了:


public   class  EMFSerializerFactory  extends  BaseSerializerFactory {
         
public  EMFSerializerFactory(Class javaType, QName xmlType) {

               
super (EMFSerializer. class , xmlType, javaType);

       }
}

 

Serializer 类需要继承 Serializer 接口:


public   class  EMFSerializer  implements  Serializer{
       
protected  Class javaType;

        
protected  QName xmlType;

        
public  SDOSerializer(Class javaType, QName xmlType) {

              
this .javaType  =  javaType;

              
this .xmlType  =  xmlType;

       }
       
public   void  serialize(QName name, Attributes attributes, Object value,

                     SerializationContext context) 
throws  IOException {

           ..
       }       
       
public  Element writeSchema(Class javaType, Types types)  throws  Exception {

             
return   null ;

       }
       
public  String getMechanismType() {
              
return  Constants.AXIS_SAX;
       }
}


我们需要实现三个方法:


1) 
serialize

   2)  getMechanismType

   3)  writeSchema


writeSchema
方法需要返回一个 XML Schema Element 对象,该 XML Schema 是指我们所用的 EMF 模型对应的 XML Schema ,我采用的 EcoreXMLSchemaBuilder 类就可以将 EPackage 对象转换成为一个 Schema Element 。不过在这里我发现好像 Axis 对这个方法并不是说非用不可,我也就没有实现,直接返回的是 NULL


getMechanismType
方法很简单

public  String getMechanismType() {

   
return  Constants.AXIS_SAX;

}



这样就可以了

 

最重要的是 serialize 方法,这个方法就是将对象进行序列化的方法:

 

public   void  serialize(QName name, Attributes attributes, Object value,
                     SerializationContext context) 
throws  IOException {

              …..
}

 

首先要将传入的 value 序列化成一个 XML 文档:

 

EPackage ePackage  =  ((EObject) value).eClass().getEPackage();

                     String targetURI 
=  ePackage.getNsURI();

                     Registry.INSTANCE.put(targetURI,ePackage);

                     Resource resource 
=  GenaralEObjectXMLResourceFactory.getInstance().createResource( null );

                     resource.getContents().add(value);

                     ByteArrayOutputStream stream 
=   new  ByteArrayOutputStream();

                     resource.save(stream, Collections.EMPTY_MAP);

 

我们将序列化后的 XML 存放到了 stream 流中,现在我们需要将这段 XML 写到 SerializationContext 对象中:


context.setWriteXMLType( null );

 context.startElement(name,attributes);

  DOMParser parser 
=   new  DOMParser();

InputSource inputSource 
=   new  InputSource();

  inputSource.setByteStream(
new  ByteArrayInputStream(stream.toByteArray()));

  parser.parse(inputSource);
  context.writeDOMElement(parser.getDocument().getDocumentElement());
                                                     context.endElement();

 

这样我们就完成了对对象的序列化。

 

4. 修改 wsdd 文件


当我们使用
Axis 提供的 WSDL2Java 工具类时, Axis 会自动给我们生成一个 wsdd 文件,而且当它发现定义的 Operation 中涉及到了复杂类型数据, Axis 会自动加上 typeMapping 元素,但是它默认给这个元素上定义的 Deserializer , Serializer 是针对 JavaBean 的,所以如果我们的类是是 EMF ,只要将 typeMapping 的类型改成 EMFSerializerFactory 以及 EMFDeserializerFactory 就可以了。


5.
总结


上面是小弟的一点愚见,请各位看官多提意见

你可能感兴趣的:(让Axis支持EMF模型)