手动实现XStream转换器工厂

手动实现XStream转换器工厂
 
XStream提供了与apache betwixt2类似的功能,缺点是在易用性差,优点是提供相当快的速度,即使个头巨大的XML也不在话下。
 
下面是一个通过XML配置装配XStream转换器工厂(方便获取XStream的工具),在大型的XML数据交换中发挥了出色的效果。本例是做复杂封装的一个测试。基本思想如此,放出来大家看看:
 
 
通常要转换的Java类是确定的。因此事先先定义一个比较复杂的Bean,然后以此为蓝本,看看转换器实现过程,以及测试效果。
 
一、目标Bean:
要转换的Java对象
public  class User { 
         private Long id; 
         private String username; 
         private String password; 
         private UserProfile userProfile =  new UserProfile(); 
         private Collection<Address> addresses =  new HashSet<Address>();
 
public  class UserProfile { 
         private  long id; 
         private String sex; 
         private Date birthday; 
         private String telphone; 
         private String email; 
         private Long userId;
 
public  class Address { 
         private Long id; 
         private String addr; 
         private String postCode;
 
 
 
二、转换器配置
主要配置类和成员的别名信息
<? xml  version ="1.0"  encoding ="GBK" ?> 

<!--  
        Document     : InfoUnits.xml.xml 
        Created on : 2008年6月9日, 下午8:10 
        Author         : leizhimin 
        Description: 转换器配置文档所约定的配置规则如下: 
                1:根元素InfoUnits包含了若干要转换目标类的配置信息. 
                2:class属性为节点所对应的类,en为节点类的属性名,元素节点名为别名信息. 
                3:每个目标类都是配置文档根节点下的直接子节点,并且有class属性,而没有en属性. 
                4:目标类下面有还可以包含更多的子元素,如果子元素类型为自定义的Class类型. 
--> 
< InfoUnits > 
         < User  class ="User" > 
                 < ID  en ="id" /> 
                 < MC  en ="username" /> 
                 < MM  en ="password" /> 
                 < UserProfile  class ="UserProfile"  en ="userProfile" > 
                         < ID  en ="id" /> 
                         < XB  en ="sex" /> 
                         < SR  en ="birthday" /> 
                         < DH  en ="telphone" /> 
                         < YJDZ  en ="email" /> 
                         < YHID  en ="userId" /> 
                 </ UserProfile > 
                 < Address-List  en ="addersses" > 
                         < Address  class ="Address" > 
                                 < ID  en ="id" /> 
                                 < DZ  en ="addr" /> 
                                 < YB  en ="postCode" /> 
                         </ Address > 
                 </ Address-List > 
         </ User > 
</ InfoUnits > 
 
 
三、转换器的构建
这是最复杂的部分,需要递归处理。
import com.thoughtworks.xstream.XStream; 
import org.dom4j.Document; 
import org.dom4j.DocumentException; 
import org.dom4j.Element; 
import org.dom4j.io.SAXReader; 

import java.io.InputStream; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.List; 
import java.util.Map; 

/** 
* 转换器配置信息工具类,加载配置信息后会生成一个转换器工厂的注册表,为整个系统提供高效的转换器 

* @author leizhimin 
*/
 
public  class InfoUnitConfigUtils { 
         //配置文档路径 
         public  static  final String CONFIG_FILE =  "/InfoUnits.xml"
         //XStream转换器注册表,也成为转换器工厂。 
         public  static  final Map<Class, XStream> converterMap =  new HashMap<Class, XStream>(); 

         static { 
                 try { 
                        reload(); 
                }  catch (ClassNotFoundException ex) { 
                        System.out.println(ex.getMessage()); 
                         throw  new RuntimeException(ex); 
                } 
        } 

         /** 
         * 重新载入配置文件信息并生成XStream转化器注册表 
         * 
         * @throws ClassNotFoundException 
         */
 
         public  static  void reload()  throws ClassNotFoundException { 
                Document document = getCfgDocument(); 
                 //遍历配置文档,构建转换器工厂。 
                 for (Iterator<Element> ite = document.getRootElement().elementIterator(); ite.hasNext();) { 
                        Element element = ite.next(); 
                        XStream xStream =  new XStream(); 
                        getAlias(element, xStream); 
                        Class clazz = Class.forName(element.selectSingleNode( "@class").getText().trim()); 
                        converterMap.put(clazz, xStream); 
                } 
        } 

         /** 
         * 获取配置信息文档对象 
         * 
         * @return 配置信息文档对象 
         */
 
         public  static Document getCfgDocument() { 
                InputStream ins = InfoUnitConfigUtils. class.getResourceAsStream(CONFIG_FILE); 
                SAXReader reader =  new SAXReader(); 
                Document doucument =  null
                 try { 
                        doucument = reader.read(ins); 
                }  catch (DocumentException ex) { 
                        System.out.println(ex.getMessage()); 
                         throw  new RuntimeException(ex); 
                } 
                 return doucument; 
        } 

         public  static Map<Class, XStream> getConverterMap() { 
                 return converterMap; 
        } 

         /** 
         * 递归设置转换器的别名信息 
         * 
         * @param element 配置文档元素节点,该节点带有一个class的属性 
         * @param xStream 转换器对象 
         * @throws ClassNotFoundException 
         */
 
         public  static  void getAlias(Element element, XStream xStream)  throws ClassNotFoundException { 
                 //如果当前元素具有class属性,则为类型节点,设置类别名 
                 if (element.selectSingleNode( "@class") !=  null) { 
                        Class clazz = Class.forName(element.selectSingleNode( "@class").getText().trim()); 
                         //带有class属性的节点名作为类的别名 
                        xStream.alias(element.getName().trim(), clazz); 
                } 
                 //如果当前元素具有en属性,则为属性节点,设置属性的别名。 
                 if (element.selectSingleNode( "@en") !=  null) { 
                         //属性所属的类 
                        Class fatherClazz = Class.forName(element.getParent().selectSingleNode( "@class").getText().trim()); 
                        xStream.aliasField(element.getName().trim(), fatherClazz, element.selectSingleNode( "@en").getText().trim()); 
                } 
                 //递归设置参数元素下所有元素的别名信息 
                List<Element> eList = element.elements(); 
                 if (eList.size() > 0) { 
                         for (Element e : eList) { 
                                getAlias(e, xStream); //递归设置 
                        } 
                } 
        } 
}
 
 
四、测试
有了转换器,就可以将射程范围(所配置的Java类)内的类以及格式匹配的XML做个双向的转换。
 
import com.thoughtworks.xstream.XStream; 

import java.util.Calendar; 
import java.util.Collection; 
import java.util.HashSet; 

/** 
* @author leizhimin 
*/
 
public  class SimpleConverter { 

         public  static  void main(String args[]) { 
                User user =  new User(100L,  "admin""123456"); 
                Calendar c = Calendar.getInstance(); 
                c.set(1982, 3, 3); 
                UserProfile profile =  new UserProfile(1000L,  "M", c.getTime(),  "66668888""admin@yahoo.com.cn", 100L); 
                Address address1 =  new Address(2000L,  "郑州市花园路""450001"); 
                Address address2 =  new Address(2001L,  "郑州市经三路""450001"); 
                Collection<Address>    co =  new HashSet<Address>(); 
                co.add(address1); 
                co.add(address2); 
                user.setAddresses(co); 
                user.setUserProfile(profile); 

                XStream xStream = InfoUnitConfigUtils.converterMap.get(User. class); 
                String xml = xStream.toXML(user); 
                System.out.println(xml); 

                User u = (User) xStream.fromXML(xml); 
                System.out.println(u); 

                String xml_address =  "        <Address>\n" + 
                                 "            <id>2001</id>\n" + 
                                 "            <addr>郑州市经三路</addr>\n" + 
                                 "            <postCode>450001</postCode>\n" + 
                                 "        </Address>"

                Address add_1 = (Address) xStream.fromXML(xml_address); 
                System.out.println(add_1.getAddr()); 


                XStream xStream1 = InfoUnitConfigUtils.getConverterMap().get(user.getClass()); 
                System.out.println( "------"); 
                System.out.println(xStream1.toXML(user)); 

                NewClass nc =  new NewClass(100L, address1); 
                nc.getUserProfiles().add(profile); 

                String nc_xml = xStream.toXML(nc); 
                System.out.println(nc_xml); 

        } 
}
 
测试结果:
<User> 
    <ID>100</ID> 
    <MC>admin</MC> 
    <MM>123456</MM> 
    <UserProfile> 
        <ID>1000</ID> 
        <XB>M</XB> 
        <SR>1982-04-03 15:55:02.621 CST</SR> 
        <DH>66668888</DH> 
        <YJDZ> admin@yahoo.com.cn</YJDZ> 
        <YHID>100</YHID> 
    </UserProfile> 
    <addresses  class= "set"
        <Address> 
            <ID>2000</ID> 
            <DZ>郑州市花园路</DZ> 
            <YB>450001</YB> 
        </Address> 
        <Address> 
            <ID>2001</ID> 
            <DZ>郑州市经三路</DZ> 
            <YB>450001</YB> 
        </Address> 
    </addresses> 
</User> 
User@1bca5f1 
郑州市经三路 
------ 
<User> 
    <ID>100</ID> 
    <MC>admin</MC> 
    <MM>123456</MM> 
    <UserProfile> 
        <ID>1000</ID> 
        <XB>M</XB> 
        <SR>1982-04-03 15:55:02.621 CST</SR> 
        <DH>66668888</DH> 
        <YJDZ> admin@yahoo.com.cn</YJDZ> 
        <YHID>100</YHID> 
    </UserProfile> 
    <addresses  class= "set"
        <Address> 
            <ID>2000</ID> 
            <DZ>郑州市花园路</DZ> 
            <YB>450001</YB> 
        </Address> 
        <Address> 
            <ID>2001</ID> 
            <DZ>郑州市经三路</DZ> 
            <YB>450001</YB> 
        </Address> 
    </addresses> 
</User> 
<NewClass> 
    <id>100</id> 
    <adderss> 
        <ID>2000</ID> 
        <DZ>郑州市花园路</DZ> 
        <YB>450001</YB> 
    </adderss> 
    <userProfiles  class= "set"
        <UserProfile> 
            <ID>1000</ID> 
            <XB>M</XB> 
            <SR>1982-04-03 15:55:02.621 CST</SR> 
            <DH>66668888</DH> 
            <YJDZ> admin@yahoo.com.cn</YJDZ> 
            <YHID>100</YHID> 
        </UserProfile> 
    </userProfiles> 
</NewClass> 

Process finished with exit code 0 
 
符合期望值,工厂构建成功!
 
 
从上面配置看,配置的代价是非常的小的,比起用Java一个一个设置更方便,更易于维护。甚至比betwixt2还简单很多。这个封装不是最简单的,仅仅是一个可行性的研究。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值