当前位置 : 主页 > 网页制作 > Nodejs >

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

来源:互联网 收集:自由互联 发布时间:2021-06-24
之前说过,对于当形参、返回值的类型是String、基本数据类型、JavaBean式的复合类,List集合,数组的时候,CXF可以很好的处理。但是像Map、非JavaBean式的复合类,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

网友评论