月蚀传说

浮躁让人失去理智
posts - 25, comments - 101, trackbacks - 0, articles - 0
  语源科技BlogJava :: 首页 ::  :: 联系 :: 聚合  :: 管理

让Axis支持EMF模型

Posted on 2006-08-30 18:23 Dart 阅读(2110) 评论(5)  编辑  收藏 所属分类: SCA 、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.
总结


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


评论

# re: 让Axis支持EMF模型  回复  更多评论   

2006-09-18 12:21 by 化工
楼主说的确实对我很有用,多谢了!@_@~~

# re: 让Axis支持EMF模型  回复  更多评论   

2008-02-27 20:03 by zyymmm
对我很有帮助啊,不过对于我这样的菜鸟来说,代码过于省略了,看着费劲

# re: 让Axis支持EMF模型  回复  更多评论   

2008-02-27 20:05 by zyymmm
还有啊,我迫切需要本篇文章涉及到的全部代码,文章中说有连接可以下载,但是我没发现啊。
楼主能不能邮寄一份给我呀,万分感谢。

我的邮箱: zyymmm@163.com

# re: 让Axis支持EMF模型  回复  更多评论   

2008-02-28 10:48 by 赵宇明
楼主,我迫切需要得到本文中所涉及到的整套代码~
程序的主干是描述清楚了,但是有些地方没有写全,所以我就搞不懂了。

拜托了,拉兄弟一把,我的email: zyymmm@163.com

# re: 让Axis支持EMF模型  回复  更多评论   

2008-02-28 11:36 by 赵宇明
哦,楼主,我发现下载链接了,不好意思。
方便的时候,能否在QQ或MSN上加我?真的非常希望能认识您这样的高手!非常真诚的,^_^