cglib与xstream结合构造webservice的xml格式的入参返参的动态生成

原创
2013/08/24 23:47
阅读数 647

cglib与xstream结合构造webservice的xml格式的入参返参的动态生成

       在做项目的时候遇到这样一个问题,需要与另一个系统进行Webservice通信,通信的入参、返参均是XML格式的,如下:

         <PARAM>
            <DET_FLAG>1</DET_FLAG>
            <IO_TASK_NO>4413051710269522</IO_TASK_NO>
          </PARAM>

       但是这样的接口很多,而且大部分都是简单的几个属性,所以不想为每一种入参和返参都新建一个POJO类,于是就想到用Cglib动态生成类,然后再利用Xstream进行转换。

       首先从网上摘了一段代码,如下:

package com.nari.component.xmlconvertor;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import net.sf.cglib.beans.BeanGenerator;
import net.sf.cglib.beans.BeanMap;


public class CglibBean {

	public Object object = null;
	
	
	public BeanMap beanMap = null;
	
	public CglibBean() {     
	      super();     
	}
	
	public CglibBean(Map<String,?> propertyMap) throws Exception {     
		
		
	      this.object = generateBean(propertyMap);     
	      this.beanMap = BeanMap.create(this.object);     
	    }
	
	
	 public void setValue(String property, Object value) {     
	      beanMap.put(property, value);     
	    } 
	 public Object getValue(String property) {     
	      return beanMap.get(property);     
	 }
	 public Object getObject() {     
	      return this.object;     
	    } 
	 
	 
	 private Object generateBean(Map<String,?> propertyMap) throws Exception  {    
	      BeanGenerator generator = new BeanGenerator();    
	      Set<String> keySet = propertyMap.keySet();     
	      for (Iterator<String> i = keySet.iterator(); i.hasNext();) {     
	       String key =  i.next();
	       //修改为获取属性的Class类型 提高兼容性
	       generator.addProperty(key,  propertyMap.get(key).getClass());     
	      }   
	      
	      return generator.create();     
	    }   
	 
	 
	
}

   这段代码就是生成了一个BeanGenerator,没什么好看的,网上一堆一堆的解释,不过将generator.addProperty的参数修改为了propertyMap.get(key).getClass()),以适应自己的需求,而且需要注意的问题是如果是web运行环境,一定要排查下依赖的jar包是否有冲突。

         然后,就是写自己的转换类了,先看下面的代码,这个是将给定的Map转换为XML格式的String

package com.nari.component.xmlconvertor;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import com.thoughtworks.xstream.XStream;


public class XMLConvertor {

	
public static String convertor(Map<String,?> map,String type) throws Exception {
		
	
		//实例化CglibBean
		CglibBean bean = new CglibBean(map);   
		//为字段赋值,如果不赋值则xml中就没有这个字段
		for(String mapkey : map.keySet()){
			 bean.setValue(mapkey, map.get(mapkey));
			 
			 System.out.println(mapkey + " = " + map.get(mapkey));
			 
			 
		}
		//定制xml格式
		Object object = bean.getObject();
        Class<?> clazz = object.getClass();     
        Field[] fields = clazz.getDeclaredFields();
        XStream x = new XStream();
        
        //修改头为PARAM
        x.alias(type, object.getClass());
        //alias属性为对应的属性名
        for (int i = 0; i < fields.length; i++) {     
            x.aliasField(fields[i].getName().substring(12,fields[i].getName().length()), object.getClass(), fields[i].getName());
        }
		//返回转换好的xml文件
        String xml = x.toXML(object);
        //不知对方接受的xml文件是否要去除空格,如果需要则可使用下面注释掉的方法
        
        //xstream有bug,当参数中含有下划线的时候会转换为双下划线,这里做简单处理
		return xml.replaceAll("__", "_");    
		
	}
	

}

注释都在类里面,需要注意的是一些xstream的alian的问题,仔细看下,没啥东西。

    其次,是将给定的XML格式字符串转换为自己需要的MAP,代码如下:

package com.nari.component.xmlconvertor;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class StringConvertor {

	
	public static Map<String,String> convertor(Map<String,String> map,String xml,String type) throws Exception {
		
		CglibBean bean = new CglibBean(map);
		
		Object obj = bean.getObject();
		
		Class<?> clazz = obj.getClass();     
		
		XStream  xs = new XStream (new DomDriver());
		xs.alias(type, clazz);
		
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {     
        	xs.aliasField(fields[i].getName().substring(12,fields[i].getName().length()), clazz, fields[i].getName());
        }
        
		Object obj2 = (Object)xs.fromXML(xml);
		
		Class<?> clazz2 = obj2.getClass();
		Method[] m3 = clazz2.getDeclaredMethods();
		for(Method method :m3){
        	
        	if(method.getName().indexOf("get")>-1){
        		String methodName = method.getName();
        		System.out.println(methodName);
        		String value = (String)method.invoke(obj2, new Object[0]);
        		String key = methodName.substring(3,methodName.length());
        		map.put(key, value);
        	}
        }
		
		
		return map;
		
		
	}
}

其实就是一个动态类的转换,注意字段的对应关系就行了。

      最后 ,是一个测试类,这里有一些xfire客户端的东西,可以删掉

package com.nari.webservice.client;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import com.nari.component.xfireclient.XFireClient;
import com.nari.component.xmlconvertor.StringConvertor;
import com.nari.component.xmlconvertor.XMLConvertor;

public class TestClient {

	public static void main(String args[]) throws Exception{

		String IO_TASK_NO = "4413051710269522";
		String DET_FLAG = "1";
		Object[] params = new Object[]{IO_TASK_NO,DET_FLAG};
		
		
		Map<String,String> m = new HashMap<String,String>();
		m.put("IO_TASK_NO", "4413051710269522");
		m.put("DET_FLAG", "1");
		
		String s = "";
		try {
			s = XMLConvertor.convertor(m, "PARAM");
			
			System.out.println(s);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		Map<String,String> ma1 = new HashMap<String,String>();
		ma1.put("IO_TASK_NO", "");
		ma1.put("DET_FLAG", "");
		
		ma1 = StringConvertor.convertor(ma1, s, "PARAM");
		System.out.println(ma1.get("IO_TASK_NO"));
		System.out.println(ma1.get("DET_FLAG"));
        
		XFireClient client = new XFireClient();
		Object[] backXml = client.callService("setInputTask", params);
		
		String backString = (String)backXml[0];
		
		
	}
}
其实,这里的入参和返参都是最基本的XML格式,没有内部属性,没有内部集合,用上面两个工具类足够了,下面的这个测试类提供了对复杂XML格式的动态转换,仅仅作为一个demo,代码如下:
package com.nari.component.xmlconvertor;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.thoughtworks.xstream.XStream;

public class TestXStream {

	public static void main(String args[]) throws Exception{
		
		//collection demo
		List<Object> list = new ArrayList<Object>();
		
		
		
		//不知道什么原因,这里new的虚拟类设置不进去值,可以用下面反射的方法想办法按照一定的规则去赋值,这个是特殊用法,一般不用,这里做兼容性支持
		//原因已经找到,是因为在MAP中使用了关键字,这点估计后台截取的时候会导致类似问题,尽量避免关键字,否则可能需要反射调用,开销很大!!
		for(int iii = 0;iii<2; iii++){
			
			
			Map<String,String> map01 = new HashMap<String,String>();
			map01.put("ASD1", "asd1");
			map01.put("CWE", "asd2");
			map01.put("ASD3", "asd3");
			
			CglibBean innerBean11 = new CglibBean(map01);
			for(String mapkey : map01.keySet()){
				innerBean11.setValue(mapkey, map01.get(mapkey));
			}
			
			Map<String,String> dymaMap = new HashMap<String,String>();
			dymaMap.put("XXXX", "AAS");
			dymaMap.put("WWWW", "ASD");
			dymaMap.put("WEWEW", "dsa");
			CglibBean dymaBean = new CglibBean(dymaMap);
			for(String mapkey : dymaMap.keySet()){
				dymaBean.setValue(mapkey, dymaMap.get(mapkey));
			}
			
			list.add(innerBean11.getObject());
			list.add(dymaBean.getObject());
			
			
			Object objo = dymaBean.getObject();
			Class<?> clazz = objo.getClass();     
	        Field[] testfield = clazz.getDeclaredFields();			
			
	        Method[] m = clazz.getDeclaredMethods();
	        
	        for(Method method :m){
	        	
	        	if(method.getName().indexOf("get")>-1){
	        		String s = (String)method.invoke(objo, new Object[0]);
	        		
	        		System.out.println("从虚拟类中中取出的值为 : " + s);
	        		
	        	}
	        }
	        
		}
		
		
		//内部属性demo
		Map<String,String> map0 = new HashMap<String,String>();
		map0.put("ASD1", "asd1");
		map0.put("ASD2", "asd2");
		map0.put("ASD3", "asd3");
		
		CglibBean innerBean1 = new CglibBean(map0);
		for(String mapkey : map0.keySet()){
			innerBean1.setValue(mapkey, map0.get(mapkey));
		}
		
		
		
		Object innerBean1Obj  = innerBean1.getObject();
		Class<?> clazz3 = innerBean1Obj.getClass();
		 Method[] m3 = clazz3.getDeclaredMethods();
	        
	        for(Method method :m3){
	        	
	        	if(method.getName().indexOf("get")>-1){
	        		String s = (String)method.invoke(innerBean1Obj, new Object[0]);
	        		
	        		System.out.println("22222从虚拟类中中取出的值为 : " + s);
	        		
	        	}
	        }
		
		
		//原始
		Map<String,Object> map = new HashMap<String,Object>();
		map.put("TEST1", "testValue1");
		map.put("TEST2", "testValue2");
		map.put("TEST3", innerBean1.getObject());
		map.put("TEST4", list);
		
		CglibBean bean = new CglibBean(map);  
		for(String mapkey : map.keySet()){
			 bean.setValue(mapkey, map.get(mapkey));
		}
		
		Object object = bean.getObject();
		
		XStream xstream = new XStream();
		Class<?> clazz = object.getClass();     
        Field[] fields = clazz.getDeclaredFields();
		
		//别名
		xstream.alias("ROOT", clazz);
		
		  for (int i = 0; i < fields.length; i++) {  
			  
			  //简化的别名
			  String aliasName = fields[i].getName().substring(12,fields[i].getName().length());
			  xstream.aliasField(aliasName, object.getClass(), fields[i].getName());
			  //如果不是String类型的则表明是Object的具体对象
			  
			  
			  //如果是List
			  if(bean.getValue(aliasName) instanceof ArrayList){
				  
				  xstream.addImplicitCollection(clazz, fields[i].getName());
				  
				  ArrayList arraylist = (ArrayList)bean.getValue(aliasName);
				  
				
				  for(Object objectBean : arraylist){
					  
					  Class<?> clazzList = objectBean.getClass();     
				      Field[] fieldsList = clazzList.getDeclaredFields();
				      
				      xstream.alias(aliasName, clazzList);
				      
				      Method[] methods = clazzList.getDeclaredMethods();
				      
				      for(Method m : methods){
				    	  
				    	  if(m.getName().indexOf("set")>-1){
				    		  
//				    		  m.invoke(objectBean, new Object[]{"tt!!!"});
				    		  
				    	  }
				    	  
				    	  
				    	  
				    	  
				      }
				      
				      for (int j = 0; j < fieldsList.length; j++) {  
						  
//				    	  String s = fieldsList[j].getName() ;
				    	  
				    	  String aliasNameInner1 = fieldsList[j].getName().substring(12,fieldsList[j].getName().length());
						  
						  xstream.aliasField(aliasNameInner1, clazzList, fieldsList[j].getName());
						  
						  xstream.useAttributeFor(clazzList,fieldsList[j].getName());
						  
				      }
				  }
				  
				  
				  
				  System.out.println(aliasName + " 是List");
			  }
			  //如果是String
			  else if(bean.getValue(aliasName) instanceof String){
				  
				  System.out.println(aliasName + " 是String");
			  }
			  //如果是内部类
			  else {
				  System.out.println(aliasName + " 是单独的类");
				  Class<?> clazzInner = bean.getValue(aliasName).getClass();     
			      Field[] fieldsInner = clazzInner.getDeclaredFields();
			      
			      for (int ii = 0; ii < fieldsInner.length; ii++) {  
					  
			    	  String aliasNameInner = fieldsInner[ii].getName().substring(12,fieldsInner[ii].getName().length());
					  
					  xstream.aliasField(aliasNameInner, clazzInner, fieldsInner[ii].getName());
					  
					  xstream.useAttributeFor(clazzInner,fieldsInner[ii].getName());
					  
			      }
			  }
			  
			  
			  
				  xstream.useAttributeFor(object.getClass(),fields[i].getName());
			 
		  }
		
		
		System.out.println(xstream.toXML(object));
		
		
	}
	
	
}

引入上面相关类后可以直接运行,运行结果如下:

<ROOT TEST1="testValue1" TEST2="testValue2">
  <TEST3 ASD2="asd2" ASD3="asd3" ASD1="asd1"/>
  <TEST4 ASD3="asd3" ASD1="asd1" CWE="asd2"/>
  <TEST4 WEWEW="dsa" WWWW="ASD" XXXX="AAS"/>
  <TEST4 ASD3="asd3" ASD1="asd1" CWE="asd2"/>
  <TEST4 WEWEW="dsa" WWWW="ASD" XXXX="AAS"/>
</ROOT>

     对于复杂XML转换为动态对象的工具类暂时没有写,以后补上

展开阅读全文
打赏
0
7 收藏
分享
加载中
更多评论
打赏
0 评论
7 收藏
0
分享
返回顶部
顶部