微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

【WebService】6.处理Map等CXF无法自动转换的值

之前说过,对于当形参、返回值的类型是String、基本数据类型、JavaBean式的复合类,List集合,数组的时候,CXF可以很好的处理。但是像Map、非JavaBean式的复合类,CXF是处理不了的。

我们先用之前编写的示例实验一下。

我们在服务端的HelloWorld接口中增加一个getAllCats()方法,返回一个Map集合:
package org.java.cxf.ws;

import java.util.List;
import java.util.Map;

import javax.jws.WebService;

import org.java.cxf.domain.Cat;
import org.java.cxf.domain.User;

@WebService
public interface HelloWorld {

    public String sayHi(String name);
    
    public List<Cat> getCatsByUser(User user);
    
    public Map<String,Cat> getAllCats();
}

在实现类中编写该方法
package org.java.cxf.ws.impl;

import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.jws.WebService;

import org.java.cxf.domain.Cat;
import org.java.cxf.domain.User;
import org.java.cxf.ws.HelloWorld;
import org.java.cxf.ws.UserService;

@WebService(endpointInterface="org.java.cxf.ws.HelloWorld",serviceName="HelloworldWs")
public class HelloworldWs implements HelloWorld{

    @Override
    public String sayHi(String name) {
        
        return name+",您好"
            +"现在的时间是:"+new Date();
    }

    @Override
    public List<Cat> getCatsByUser(User user) {
        //在实际项目中,Web Service组件自己并不会去实现业务功能
        //它只是调用业务逻辑组件的方法来暴露Web Service
        UserService us=new UserServiceImpl();
        return us.getCatsByUser(user);
    }

    @Override
    public Map<String,Cat> getAllCats() {
        UserService us=new UserServiceImpl();
        return us.getAllCats();
    }
}

然后在UserService接口声明该方法
package org.java.cxf.ws;

import java.util.List;
import java.util.Map;

import org.java.cxf.domain.Cat;
import org.java.cxf.domain.User;

public interface UserService {

    public List<Cat> getCatsByUser(User user);

    public Map<String,Cat> getAllCats();

}

实现类UserServiceImpl中实现getAllCats()方法
package org.java.cxf.ws.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.java.cxf.domain.Cat;
import org.java.cxf.domain.User;
import org.java.cxf.ws.UserService;

public class UserServiceImpl implements UserService {
    
    //用一个HashMap来模拟内存中的数据库
    static Map<User,List<Cat>> catDb=new HashMap<>();
    
    //初始化数据
    static{
        List<Cat> CatList1=new ArrayList<Cat>();
        CatList1.add(new Cat(1,"花花","黑色"));
        CatList1.add(new Cat(2,"毛球","白色"));
        catDb.put(new User(1,"zhangsan","1122","河南"),CatList1);
        
        List<Cat> CatList2=new ArrayList<Cat>();
        CatList2.add(new Cat(3,"丁丁","黄色"));
        CatList2.add(new Cat(4,"咪咪","灰色"));
        catDb.put(new User(1,"lisi","3344","广州"),CatList2);
    }
    
    
    public List<Cat> getCatsByUser(User user) {
        return catDb.get(user);
    }


    @Override
    public Map<String,Cat> getAllCats() {
        Map<String,Cat> result =new HashMap<String,Cat>();
        int i=1;
        for(List<Cat> cats:catDb.values()){
            for(Cat cat:cats){
                result.put("第"+ i++ +"个",cat);
            }
        }
        return result;
    }
}

我们重新启动服务端:



发现分别报了javax.xml.ws.WebServiceException和 javax.xml.bind.JAXBException这两个错误。也就是说Map这种类型是CXF接受不了的数据格式。

那么,在CXF开发中,如果遇到系统无法自动处理的类型,就需要开发人员自行处理了。
处理思路:提供一个转换器,该转换器负责把CXF搞不定的类型,转换成其搞的定的类型。
处理步骤:
(1)使用@XmlJavaTypeAdapter(java类型适配器注解)修饰CXF无法自动处理的类型。使用该注解时,需要给注解指定一个适配器值。
package org.java.cxf.ws;

import java.util.List;
import java.util.Map;

import javax.jws.WebService;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import org.java.cxf.domain.Cat;
import org.java.cxf.domain.User;

@WebService
public interface HelloWorld {

    public String sayHi(String name);
    
    public List<Cat> getCatsByUser(User user);
    
    //CXF不能处理这种类型,所以采用XmlJavaTypeAdapter来处理
    public @XmlJavaTypeAdapter(value = WSXmlAdapter.class)
            Map<String,Cat> getAllCats();
}

(2)实现自己的适配器,然后编写一个CXF识别的数据类型:
package org.java.cxf.ws;

import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.annotation.adapters.XmlAdapter;

import org.java.cxf.domain.Cat;
import org.java.cxf.ws.StringCat.Entry;

//XmlAdapter<ValueType,BoundType>接口参数中ValueType是CXF可以解析的类型
//BoundType是CXF搞不定的类型
public class WSXmlAdapter extends XmlAdapter<StringCat,Map<String,Cat>>{

    @Override
    public Map<String,Cat> unmarshal(StringCat v) throws Exception {
        Map<String,Cat> result=new HashMap<>();
        for(Entry entry:v.getEntries()){
            result.put(entry.getKey(),entry.getValue());
        }
        return result;
    }

    @Override
    public StringCat marshal(Map<String,Cat> v) throws Exception {
        StringCat cCat=new StringCat();
        for(String key:v.keySet()){
            cCat.getEntries().add(new Entry(key,v.get(key)));
        }
        return cCat;
    }
    
}

其中StringCat类为:
package org.java.cxf.ws;

import java.util.ArrayList;
import java.util.List;

import org.java.cxf.domain.Cat;

public class StringCat {
    
    //静态内部类
    public static class Entry{
        private String key;
        private Cat value;
        public Entry(){}
        public Entry(String key,Cat value) {
            this.key=key;
            this.value=value;
        }
        public String getKey() {
            return key;
        }
        public void setKey(String key) {
            this.key = key;
        }
        public Cat getValue() {
            return value;
        }
        public void setValue(Cat value) {
            this.value = value;
        }
    }
    
    private List<Entry> entries=new ArrayList<Entry>();

    public List<Entry> getEntries() {
        return entries;
    }

    public void setEntries(List<Entry> entries) {
        this.entries = entries;
    }
    
}

然后我们的程序就完成了,我们重启服务端看一下:



我们的服务重启成功。

我们分析一下发布成功时的WSDL文档:


整个wsdl文档没有什么变化,只是比之前多了一个getAllCats的operation。

我们去看一下import标签中的location的路径,之前说过,这个是引入的接口的定义文档:



我们看到这里也没有什么大的变化,仅仅是多了getAllCats的message。
我们去看一下types标签对的import标签中的schemaLocation,这是引入的类型的定义文档:



从中我们可以看出我们定义的StringCat数据类型的定义,证明我们对外发布的getAllCats方法的返回类型是StringCat。

即是,对于getAllCats操作来说,
传入的消息是
<getAllCats>
  </getAllCats>

传出的消息是
  <getAllCatsResponse>
      <return>
     <entries>
            <key>字符串</key>
            <value>
                <color>字符串</color>
                <id>整数值</id>
                <name>字符串</name>
            </value>
         </entries>
      </return>
  </getAllCatsResponse>

我们在客户端的源代码文件重新生成WebService相关代码:



然后我们刷新客户端工程:



我们在客户端的主类中调用getAllCats方法获取所有的Cat:
package show;

import org.java.cxf.ws.Entry;
import org.java.cxf.ws.HelloWorld;
import org.java.cxf.ws.StringCat;
import org.java.cxf.ws.impl.HelloworldWs;

public class ClientMain {
    public static void main(String[] args) {
        HelloworldWs factory=new HelloworldWs();
        //此处返回的只是远程Web Service的代理
        HelloWorld hw=factory.getHelloworldWsPort();
        System.out.println(hw.sayHi("孙悟空"));
        
        StringCat cCat=hw.getAllCats();
        for (Entry entry: cCat.getEntries()) {
            System.out.println(entry.getKey()+entry.getValue().getName());
        }
    }
}

运行结果:
孙悟空,您好现在的时间是:Sun Jul 31 17:52:52 CST 2016
第4个毛球
第3个花花
第1个丁丁
第2个咪咪

注:不是顺序的原因是,hashMap是通过Hash值排序的而不是通过Key排序。

至此我们使用适配器,解决了所有CXF解决不了的数据结构类型。

转载请注明出处:http://www.voidcn.com/article/p-eqqmdopj-bbb.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐