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 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 类即可。
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 void onEndElement(String namespace, String localName,
DeserializationContext context) throws SAXException {
…….
}
}
在 onEndElement 方法中的参数有一个 DeserializationContext 对象,我们可以通过它获得 XML 文档片段:
该 Document 就是得到的 XML 片段。
然后我们通过 EMF 提供的 Resource 对象将 XML 文档进行反序列化(这里我自己写了一个创建 XMLResourceImpl 的工厂类,读者可以在文章后面下载代码):
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 获得:
value = sdoValue;
这里需要注意的是,如果我们生成的 EMF 模型是通过 XSD 来生成的,那反序列化后得到的应该是一个 DocumentRoot 对象,该对象可能并不是我们想得到的,比如:
< student name =”me”/>
得到的对象并是不 Student 对象,而是一个 DocumentRoot ,而 Student 对象是包含在 DocuementRoot 对象的子对象中。
但是如果是直接通过 Ecore 模型生成的则不会有 DocumentRoot 对象。这里如何去判断得到的对象是不是 DocumentRoot 需要读者进一步去看看 EMF ,这里我没有给出通用的手段解决,只是根据我本身程序的需求去获得的:
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 EMFSerializerFactory(Class javaType, QName xmlType) {
super (EMFSerializer. class , xmlType, javaType);
}
}
Serializer 类需要继承 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 方法很简单
return Constants.AXIS_SAX;
}
这样就可以了
最重要的是 serialize 方法,这个方法就是将对象进行序列化的方法:
SerializationContext context) throws IOException {
…..
}
首先要将传入的 value 序列化成一个 XML 文档:
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.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. 总结
上面是小弟的一点愚见,请各位看官多提意见