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.
總結
上面是小弟的一點愚見,請各位看官多提意見