CXF+JAXB处理复杂数据

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适配器,代码如下:

MapAdapter.java

MapEntity是自己定义的一个简单结构,用于保持Map中的key-value关系:

MapEntity.java

public class MapEntity{
     public Object key;
     public Object value;
}

经过这样的处理,就能够实现Map与XML之间的映射。

3.2 断开循环引用的回路

对象之间的引用很有可能出现回路。最简单的情况是两个对象之间互相引用。这在ORM中很常见。如果我们在前面的User类中增加父子关系,如下:

User.java

当同时在两个方向设置引用关系时,就发生了循环引用:

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恢复出对象后,会自动调用这个方法。我们可以在方法中将丢失的信息补全:

afterUnmarshal方法

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

@XmlSeeAlso({
    MyUser.class
})
public class User {...}

4 代码

本文相关的所有代码如下:

4.1 maven工程文件

pom.xml

<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适配器

MapAdapter.java

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结构

MapEntity.java

package com.hysec.utils.jaxb;

public class MapEntity{
    public Object key;
    public Object value;
}

4.4 JavaBean父类

User.java

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子类

MyUser.java

package cxfdemo;

public class MyUser extends User {
    public String myProp;
}

4.6 webservice接口定义

CXFDemo.java

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实现类

CXFDemoImpl.java

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 测试用例

TestEndpoint.java

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);
    }

}

时间: 2024-10-10 07:44:42

CXF+JAXB处理复杂数据的相关文章

cxf 创建动态webService

D:\developTools\apache-cxf-2.5.2\samples\wsdl_first_dynamic_client CXF 方法 cxf方法 serviceInfo.getBindings() BindingInfo--[BindingInfo http://schemas.xmlsoap.org/wsdl/soap/] serviceInfo.getDocumentation() WS_0917_05 package com.test; import java.beans.P

CXF之&quot;@XmlType.name 和 @XmlType.namespace 为类分配不同的名称&quot;错误

CXF 的 wsdl2java.bat 生产的代码,拷贝到目录,进行调研 web service接口时,抛出错误: Exception in thread "main" javax.xml.ws.WebServiceException: org.apache.cxf.service.factory.ServiceConstructionException at org.apache.cxf.jaxws.ServiceImpl.getPort(ServiceImpl.java:347)

CXF生成client注意事项

1. 在使用wsdl2java命令生成client文件时在Service的Java文件中面出现super构造错误,这是因为jax-ws2.2规约与java6冲突  故须要减少jax-ws规约版本号. 解决方法:wsdl2java -frontend jaxws21  http://localhost:8080/MyWebService? WSDL    生成client文件    2.  在使用wsdl2java生成的client文件  .假设我们改动了包的名称就会出现       Except

CXF生成客户端注意事项

1. 在使用wsdl2java命令生成客户端文件时在Service的Java文件里面出现super构造错误,这是由于jax-ws2.2规约与java6冲突  故需要降低jax-ws规约版本. 解决方法:wsdl2java -frontend jaxws21  http://localhost:8080/MyWebService?WSDL    生成客户端文件    2.  在使用wsdl2java生成的客户端文件  ,如果我们修改了包的名称就会出现       Exception in thre

CXF 实现 webservice 并且部署在web项目中 tomcat作为容器

在tomcat作为容器发布webservice服务前,我们先来看一个简单的不通过容器即可发布服务的例子 package com.tree.webservice; import javax.jws.WebService; @WebService public interface HelloWorld { public String sayHello(String content); } package com.tree.webservice.impl; import javax.jws.WebSe

异构系统间Web Service通讯框架小结(补完企划)

本文不讨论WS性能问题,也不讨论使用非框架方式比如TCP伪造HTTP协议等方式通讯,仅讨论在常见的系统间使用HTTP+SOAP通讯框架的方法以及雷区. 本文大多数内容来自于某次在两个月内迁移完两个异构ESB产品后的思考. Java Java下框架比较多,常见问题是一种获取(HTTP GET) WSDL文件然后框架动态编译,再POST服务.在不计较性能的情况下没有什么问题.如果获取到的WSDL中服务URL不可访问,则框架就会产生错误.如果不能修改源码改为直接POST方式,一种解决方案是将手工下载的

Java 新特性(5) - Java EE 5 新特性

Java EE 5 由 Java Community Process 通过 Java Specification Request 244 发布,这个 “总纲” JSR 指出了详细描述 Java EE 5 中各种技术的其他规范(参见 参考资料).Sun Microsystems 的 Bill Shannon 领导一个专家组制订了这个规范,这个专家组包括从 IT 行业重量级人物到个人专家的 31 位成员.以前的 Java EE 版本有: J2EE 1.2(于 1999 年 12 月发布):这是第一个

Java EE 7 教程 第一部分 简介 第1章 概述 第1.8节 Java平台中的Java EE 7 API, 标准版7

原文:http://docs.oracle.com/javaee/7/tutorial/doc/overview008.htm 翻译:石卓林 [email protected] 1.8 Java EE 7 APIs in the Java Platform, Standard Edition 7 Several APIs that are required by the Java EE 7 platform are included in the Java Platform, Standard

[CXF REST标准实战系列] 一、JAXB xml与javaBean的转换

Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket Reprint it anywhere u want. 文章Points: 1.不认识到犯错,然后得到永久的教训. 2.认识JAXB 3.代码实战 1.不认识到犯错,然后得到永久的教训. 也不是所谓的教训吧,真正的教训来自于对错误的剖析理解很深刻.然后有种"吃一堑,长一智"的感觉才叫教训.近日和团队工头们用CXF3.0和Spring4.0开发一个平台,模仿着第三方支付,用xml进行数据交互