CXF+JAXB处理复杂数据
CXF默认使用JAXB 来实现对象和XML之间的映射。在前面的例子 中,使用CXF发布的Webservice,其方法的参数和返回值都是简单类型。 本文讨论对象复杂性的分级,验证对于各种复杂度JAXB的支持情况,以及使用JAXB时对于Map,循环引用,继承等情况的处理办法。 文中的例子没有直接调用JAXB的API,而是用CXF发布webservice的形式验证对象到xml的marshal和unmarshal, 所以本文也可以作为使用CXF的参考资料。
Table of Contents
1 数据复杂性的分类
大体来说,Java中的数据/数据对象按照其复杂度可以分为以下几类:
1.1 简单数据类型
包括基本类型和Java对基本类型的封装,主要有:
基本类型 | 封装类 |
---|---|
float | Float |
double | Double |
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
boolean | Boolean |
char[] | String |
1.2 自定义类型
在C里面叫做struct,在Java里面叫做JavaBean,包含自定义属性和getter/setter方法。
1.3 集合类型
Java的集合类(Collection)主要分为List,Set,Map三个系列。List实现了元素的序列(顺序),Set实现不重复的集合,Map实现了key-value的映射。
1.4 复杂类型
更复杂的情况是对于上述三种类型的组合运用,比如在自定义类型中使用集合,或者集合的嵌套等。 复杂类型还会涉及到循环引用和继承关系等问题。
2 JAXB对数据复杂性的支持
- 简单类型
对于简单的数据类型,JAXB不需要任何处理就完全能够支持
- 自定义类型
JAXB对于一般的JavaBean也能够支持,比如下面的例子:
User.java
public class User {
private Integer id;
private String name; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}
不需要JavaBean实现Serializable接口,也不需要增加@XmlRootElement声明。
- 集合类型
JAXB能够内置支持List和Set集合,但是对于Map的支持需要自己处理。
- 复杂类型
JAXB支持简单类型、自定义类型、集合类型等的嵌套,但是对于循环引用、继承等情况需要增加额外的处理。
3 常用技巧
3.1 使用自定义的XmlAdapter支持Map
JAXB可以在变量上添加@XmlJavaTypeAdapter标注,指定对该变量专门的适配器进行处理。 适配器继承XmlAdapter类,并覆盖了marshal和unmarshal方法,分别用于对象到XML的映射和XML到对象的映射。
使用XmlAdapter可以实现对Map类型的映射。
比如对于要通过CXF发布的WebService接口方法上,可以增加标注:
@XmlJavaTypeAdapter(MapAdapter.class)
Map<String,User> getUserMap(); Integer setUserMap(@XmlJavaTypeAdapter(MapAdapter.class)Map<String,User> users);
其中的MapAdapter就是自己实现的Map适配器,代码如下:
MapEntity是自己定义的一个简单结构,用于保持Map中的key-value关系:
public class MapEntity{
public Object key;
public Object value;
}
经过这样的处理,就能够实现Map与XML之间的映射。
3.2 断开循环引用的回路
对象之间的引用很有可能出现回路。最简单的情况是两个对象之间互相引用。这在ORM中很常见。如果我们在前面的User类中增加父子关系,如下:
当同时在两个方向设置引用关系时,就发生了循环引用:
child.parent = parent;
parent.children.put(child.getName(), child);
发生循环引用时,JAXB就会抛出异常。而处理的办法就是断开其中一个方向的引用。具体做法就是使用@XmlTransient标注,表明该属性在marshal是不作处理。 如上面的User中,我们可以只处理parent到child的引用,而不处理child到parent的引用:
@XmlTransient
public User parent;
这样虽然解决了循环引用的问题,但是会导致得到User对象的parent属性为null。为使用带来不变。 解决的办法是在JavaBean中增加afterUnmarshal()方法,当JAXB从xml恢复出对象后,会自动调用这个方法。我们可以在方法中将丢失的信息补全:
public void afterUnmarshal(Unmarshaller u,Object parent) {
for(Iterator itor = this.children.values().iterator();itor.hasNext();){
User user = (User)itor.next();
user.parent = this;
}
}
3.3 使用@XmlSeeAlso标注处理继承关系
继承关系在ORM中已经处理得非常完善了,JAXB处理继承关系更加简单,只需要在继承树的根类上增加@XmlSeeAlso标注,声明所有的子类即可。 比如我们定义了一个User的子类:
public class MyUser extends User {...}
则只需要在User类上面增加标注:
@XmlSeeAlso({
MyUser.class
})
public class User {...}
4 代码
本文相关的所有代码如下:
4.1 maven工程文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.hysec</groupId>
<artifactId>cxfdemo</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>cxfdemo</name> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>apache-cxf</artifactId>
<version>2.4.1</version>
<type>pom</type>
</dependency>
</dependencies>
</project>
4.2 Map适配器
package com.hysec.utils.jaxb; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set; import javax.xml.bind.annotation.adapters.XmlAdapter; public class MapAdapter extends XmlAdapter<MapEntity[], Map> { @Override
public MapEntity[] marshal(Map map) throws Exception {
// TODO Auto-generated method stub
MapEntity[] list = new MapEntity[map.size()];
Set keyset = map.keySet();
int index =0;
for(Iterator itor=keyset.iterator();itor.hasNext();){
MapEntity item = new MapEntity();
item.key = itor.next();
item.value = map.get(item.key);
list[index++] = item;
}
return list;
} @Override
public Map unmarshal(MapEntity[] list) throws Exception {
// TODO Auto-generated method stub
Map map = new HashMap();
for(int i=0;i<list.length;i++){
MapEntity item = list[i];
map.put(item.key, item.value);
} return map; } }
4.3 Map适配器使用的key-value结构
package com.hysec.utils.jaxb; public class MapEntity{
public Object key;
public Object value;
}
4.4 JavaBean父类
package cxfdemo; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient; @XmlSeeAlso({
MyUser.class
})
public class User { private Integer id; private String name; @XmlTransient
public User parent; public Map<String,User> children = new HashMap<String,User>(); public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public void afterUnmarshal(Unmarshaller u,Object parent) {
for(Iterator itor = this.children.values().iterator();itor.hasNext();){
User user = (User)itor.next();
user.parent = this;
} } }
4.5 JavaBean子类
package cxfdemo; public class MyUser extends User {
public String myProp;
}
4.6 webservice接口定义
package cxfdemo; import java.util.List;
import java.util.Map;
import java.util.Set; import javax.jws.WebService;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import com.hysec.utils.jaxb.MapAdapter; @WebService
public interface CXFDemo {
String sayHello(String foo);
String sayHelloToUser(User user);
User getUser(String name); List<User> getUsers();
Integer setUsers(List<User> users); Set<User> getUserSet();
Integer setUserSet(Set<User> users); @XmlJavaTypeAdapter(MapAdapter.class)
Map<String,User> getUserMap();
Integer setUserMap(@XmlJavaTypeAdapter(MapAdapter.class)Map<String,User> users); User addChild(User parent,User child);
}
4.7 webservice实现类
package cxfdemo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set; import javax.jws.WebService; @WebService()
public class CXFDemoImpl implements CXFDemo { public String sayHello(String foo) {
return "hello "+foo;
} public String sayHelloToUser(User user){
return "hello "+user.getName();
} public User getUser(String name){
User user = new User();
user.setName(name);
return user;
} public List<User> getUsers(){
List<User> users = new ArrayList<User>();
users.add(new User());
return users;
} public Integer setUsers(List<User> users){
return users.size();
} public Set<User> getUserSet(){
Set<User> set = new HashSet<User>();
set.add(new User());
set.add(new User());
return set;
}
public Integer setUserSet(Set<User> users){
return users.size();
} public Map<String,User> getUserMap(){
HashMap<String,User> map = new HashMap<String,User>();
User user1 = new User();
user1.setName("Holbrook");
map.put("Holbrook", user1); User user2 = new User();
user2.setName("wanghaikuo");
map.put("wanghaikuo", user2); return map;
} public Integer setUserMap(Map<String,User> users){
return users.size();
} public User addChild(User parent,User child){
child.parent = parent;
parent.children.put(child.getName(), child);
return parent;
}
}
4.8 测试用例
package cxfdemo.test; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set; import javax.xml.ws.Endpoint; import junit.framework.Assert;
import junit.framework.TestCase; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import cxfdemo.CXFDemo;
import cxfdemo.CXFDemoImpl;
import cxfdemo.MyUser;
import cxfdemo.User; public class TestEndpoint extends TestCase { private static final String ADDRESS = "http://localhost:9000/cxfdemo";
private static CXFDemo service; @Override
protected void setUp() throws Exception {
// TODO Auto-generated method stub
super.setUp();
if(null==service){
System.out.println("Starting Server");
CXFDemoImpl demo = new CXFDemoImpl(); Endpoint.publish(ADDRESS, demo);
System.out.println("Start success"); JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(CXFDemo.class);
factory.setAddress(ADDRESS);
service = (CXFDemo)factory.create();
} } public void testSayHello(){
Assert.assertEquals(service.sayHello("foo"), "hello foo");
} public void testSayHelloToUser(){ User user = new User();
user.setName("Holbrook");
String result = service.sayHelloToUser(user);
Assert.assertEquals(result,"hello Holbrook");
} public void testGetUser(){
User user = service.getUser("Holbrook");
Assert.assertEquals("Holbrook",user.getName());
} public void testGetUsers(){
List<User> users = service.getUsers();
Assert.assertEquals(1,users.size());
} public void testSetUsers(){
List<User> users = new ArrayList<User>();
users.add(new User());
users.add(new User());
users.add(new User()); Assert.assertEquals(3,service.setUsers(users).intValue());
} public void testGetUserSet(){
Set<User> userSet = service.getUserSet();
Assert.assertEquals(2,userSet.size());
} public void testSetUserSet(){
Set<User> set = new HashSet<User>();
set.add(new User());
set.add(new User()); Assert.assertEquals(2, service.setUserSet(set).intValue());
} public void testGetUserMap(){
Map<String,User> map = service.getUserMap();
Assert.assertTrue(map.containsKey("Holbrook"));
Assert.assertTrue(map.containsKey("wanghaikuo"));
} public void testSetUserMap(){
HashMap<String,User> map = new HashMap<String,User>();
User user1 = new User();
user1.setName("Holbrook");
map.put("Holbrook", user1); User user2 = new User();
user2.setName("wanghaikuo");
map.put("wanghaikuo", user2); Assert.assertEquals(2, service.setUserMap(map).intValue());
} public void testAddChild(){
User root = new User();
root.setName("root");
//root.parent = root; User child = new User();
child.setName("child");
User parent = service.addChild(root, child); Assert.assertTrue(parent.children.containsKey("child"));
Assert.assertEquals(parent.children.get("child").parent, parent);
} public void testInheritance(){
User parent = new User();
MyUser child = new MyUser();
child.setName("child");
child.myProp = "subclass Prop";
User root = service.addChild(parent, child); User newChild = root.children.get("child");
System.out.println(newChild instanceof MyUser);
System.out.println(((MyUser)newChild).myProp);
} }
CXF+JAXB处理复杂数据的更多相关文章
- "转" CXF+JAXB处理复杂数据
CXF简单数据类型以及类(JavaBean)都提供了较好的支持. 但是对于一些复杂类型(集合或者Map的嵌套)的处理时,就需要我们进行“”人工干预“.在网上找了一些文章,其中这篇写的最为详细,再次备注 ...
- webservice cxf error:类的两个属性具有相同名称 "password"
execption detail: Caused by: javax.xml.ws.WebServiceException: org.apache.cxf.service.factory.Servic ...
- cxf 报错:java.lang.NoSuchMethodError: org.apache.ws.commons.schema.XmlSchemaCollection.read(Lorg/w3c/dom/Document;Ljava/lang/String;)
由于没有仔细查看官方提供的文档,由jdk版本不一致导致的出错: http://cxf.apache.org/cxf-316-release-notes.html 自己使用的是jdk1.8. 报Exce ...
- CXF之"@XmlType.name 和 @XmlType.namespace 为类分配不同的名称"错误
CXF 的 wsdl2java.bat 生产的代码,拷贝到目录,进行调研 web service接口时,抛出错误: Exception in thread "main" javax ...
- org.apache.cxf.interceptor.Fault: Unmarshalling Error: 意外的元素 (uri:"", local:"mixornot")。
三月 09, 2018 3:09:14 下午 org.apache.cxf.phase.PhaseInterceptorChain doDefaultLogging警告: Interceptor fo ...
- org.apache.cxf.interceptor.Fault: Unmarshalling Error: 意外的元素 (uri:"", local:"werks")。所需元素为(none)
警告: Interceptor for {http://impl.service.ws.cxf.com/}WsStkoServiceImplService#{http://service.ws.cxf ...
- 使用CXF+Spring发布WebService,启动报错
使用CXF+Spring发布WebService,启动报错,日志如下: 五月 12, 2017 9:01:37 下午 org.apache.tomcat.util.digester.SetProper ...
- cxf 创建动态webService
D:\developTools\apache-cxf-2.5.2\samples\wsdl_first_dynamic_client CXF 方法 cxf方法 serviceInfo.getBindi ...
- CXF生成client注意事项
1. 在使用wsdl2java命令生成client文件时在Service的Java文件中面出现super构造错误,这是因为jax-ws2.2规约与java6冲突 故须要减少jax-ws规约版本号. ...
随机推荐
- Excel的vlookup函数的用法
VLOOKUP函数用于搜索指定区域内首列满足条件的元素,确定待检测单元格在区域中的行序号,再进一步返回选定单元格的值. 为了讲解的需要,特制作如图所示的表格.当然,大家也可以根据自己的实际情况设计合适 ...
- Rust by Example1
最近在学习Rust,想通过这个记录一下学习过程 首先是老规矩:HelloWorld // Rust单行注释 /// 文档注释 /* 多行注释 */ fn main() { // 这是个 ...
- 三、cent OS安装配置nginx
简介Tengine是淘宝发起的web服务器项目,简单的讲就是对nginx进行了二次开发并提供了更丰富的功能,官网地址:http://tengine.taobao.org/ 下载nginx这里使用淘宝二 ...
- layui对json数据的格式要求
layui有自己的一套特定的数据格式交互,必须参数code:0,msg:“”,count:数据size(int),data:”数据List”.**一般我们选择封装返回接收类**. 若想要绑定数据到la ...
- 使用命令行编译打包运行自己的MapReduce程序 Hadoop2.6.0
使用命令行编译打包运行自己的MapReduce程序 Hadoop2.6.0 网上的 MapReduce WordCount 教程对于如何编译 WordCount.java 几乎是一笔带过… 而有写到的 ...
- 零基础学python习题 - 进入python的世界
1. python拥有以下特性:面向对象的特性.动态性.内置的数据结构.简单性.健壮性.跨平台性.可扩展性.强类型语言.应用广泛 2. python 需要 编译 3. 以下不属于python内置数据 ...
- 一步一步实现web程序信息管理系统之三----登陆业务逻辑实现(验证码功能+参数获取)
本篇紧接着上一篇文章[一步一步实现web程序信息管理系统之二----后台框架实现跳转登陆页面] 验证码功能 一般验证码功能实现方式为,前端界面访问一个url请求,后端服务代码生成一个图片流返回至浏览器 ...
- jquery特效:无缝向上循环滚动列表
效果呈现 整个列表间隔设定的时间向上移动一个item的高度 html结构: <div class="slide-title"> <span>title1&l ...
- ThreeJS geometry的顶点世界坐标
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 前端把html表格生成为excel表格
最近公司改后台管理系统.要求导出台账项目等等为excel表格,找半天还真有,他是通过query.table2excel.js 实现,原谅我原生不会弄这个当然大家有可以给我留言. <!DOCTYP ...