zoukankan      html  css  js  c++  java
  • JAVA bean与XML互转的利器---XStream

    最近在项目中遇到了JAVA bean 和XML互转的需求, 本来准备循规蹈矩使用dom4j忽然想起来之前曾接触过的XStream, 一番研究豁然开朗,利器啊利器, 下来就XStream的一些用法与大家分享。

    XStream是大名鼎鼎的thought works下的一个开源项目, 主要功能是提供JAVA bean 和XML文本之间的转换,另外还提供JAVA bean和JSON之间的转换,这个不在本次讨论的范围内。

    XStream进行转换是非常简单的,对JAVA bean没有任何要求:

    • 不要求对private属性提供access方法(set/get)。
    • 不要求提供默认构造函数。
    实际的代码操作就更简单了,在JAVA1.5以后XSteam也支持了annotation。 这时就只要在JAVA BEAN中添加若干annotation就可以了,当然如果不允许修改JAVA bean, 那XStream也提供register的方式,也是很简单的。 我准备在例子中体现一下的topic:
    • 基本转换
    • 对象起别名
    • 处理属性
    • 处理List
    • 忽略field
    1. 基本转换
    这是一个普通的JAVA bean:
    package xstreamTest;  
    public class Person {  
        private String name;  
        private int age;  
      
        public int getAge() {  
            return age;  
        }  
      
        public void setAge(int age) {  
            this.age = age;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public String getName() {  
            return this.name;  
        }  
    }  
    

      转换代码是这样的:

    XStream xstream = new XStream();  
    Person person = new Person();  
    person.setName("pli");  
    person.setAge(18);  
    System.out.println(xstream.toXML(person));  

    我们得到了这样的结果:

    <xstreamTest.Person>  
      <name>pli</name>  
      <age>18</age>  
    </xstreamTest.Person>  
    有没有觉得很奇怪为什么会有“xstreamTest.Person”的标签?对照下上面提到的JAVA bean这个标签是来自于JAVA bean的类全路径的。
    可是这个并不是我想要的啊,有没办法改变?有,简单吗? 简单!
     
    2. 起别名
    家丁我们希望将“xstreamTest.Person” 这个莫名其妙的element标签改为“person”我们应该这么做。
    package xstreamTest;  
    @XStreamAlias("person")  
    public class Person {  
        private String name;  
        private int age;  
      
        public int getAge() {  
            return age;  
        }  
      
        public void setAge(int age) {  
            this.age = age;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public String getName() {  
            return this.name;  
        }  
    }  

    而执行代码会变成这样:

    XStream xstream = new XStream();  
    xstream.autodetectAnnotations(true);  
    Person person = new Person();  
    person.setName("pli");  
    person.setAge(18);  
    System.out.println(xstream.toXML(person));  

    这样我们就得到了想要的:

    <person>  
      <name>pli</name>  
      <age>18</age>  
    </person>  
    这里要提到的是“xstream.autodetectAnnotations(true);” 这句代码告诉XStream去解析JAVA bean中的annotation。这句代码有一个隐患,会在后面讨论。
    别名可以改变任何你想在序列化时改变的对象名字,类,属性甚至包名,所用到的其实就是“XSstreamAlias”这个annotation。
     
    3. 处理属性
    如果想要将JAVA bean中的“age”属性作为XML中person标签的一个attribute该怎么办呢。
    这里介绍另外一个annotation:@XStreamAsAttribute, 我们的JAVA bean变成了这样:
    @XStreamAlias("person")  
    public class Person {  
        private String name;  
        @XStreamAsAttribute  
        private int age;  
          
        public int getAge() {  
            return age;  
        }  
      
        public void setAge(int age) {  
            this.age = age;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public String getName() {  
            return this.name;  
        }  
    }  

    结果是这样的:

    <person age="18">  
      <name>pli</name>  
    </person>  
    好玩吧。
     
    4. 处理List
    如果JAVA bean中有List是什么情形呢。
    @XStreamAlias("person")  
    public class Person {  
        private String name;  
        @XStreamAsAttribute  
        private int age;  
          
        List<String> girlFriends;  
          
        public List<String> getGirlFriends() {  
            return girlFriends;  
        }  
      
        public void setGirlFriends(List<String> girlFriends) {  
            this.girlFriends = girlFriends;  
        }  
      
        public int getAge() {  
            return age;  
        }  
      
        public void setAge(int age) {  
            this.age = age;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public String getName() {  
            return this.name;  
        }  
    }  

    直接转换我们会得到这样的结果:

    <person age="18">  
      <name>pli</name>  
      <girlFriends>  
        <string>YuanYuanGao</string>  
        <string>QiShu</string>  
        <string>BoZhiZhang</string>  
      </girlFriends>  
    </person>  

    结果其实也不赖,XStream在这里提供了一个@XStreamImplicit(itemFieldName=***)的annotation来满足用户想将List的根节点去掉和改变列表名字的需求,对应到我们的例子上就是去掉<girlFriends>标签和改变"<string>".我们来看看效果。

    @XStreamAlias("person")  
    public class Person {  
        private String name;  
        @XStreamAsAttribute  
        private int age;  
        @XStreamImplicit(itemFieldName="girl")  
        List<String> girlFriends;  
          
        public List<String> getGirlFriends() {  
            return girlFriends;  
        }  
      
        public void setGirlFriends(List<String> girlFriends) {  
            this.girlFriends = girlFriends;  
        }  
      
        public int getAge() {  
            return age;  
        }  
      
        public void setAge(int age) {  
            this.age = age;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public String getName() {  
            return this.name;  
        }  
    }  

    结果是这样:

    <person age="18">  
      <name>pli</name>  
      <girl>YuanYuanGao</girl>  
      <girl>QiShu</girl>  
      <girl>BoZhiZhang</girl>  
    </person>
    5. 忽略属性
    如果在JAVA bean中有些属性不想被序列化,XStream提供了解决这个需求的annotation: @XStreamOmitField
    比如说不想讲girlfriends这个List序列化
    @XStreamAlias("person")  
    public class Person {  
        private String name;  
        @XStreamAsAttribute  
        private int age;  
        @XStreamImplicit(itemFieldName="girl")  
        @XStreamOmitField  
        List<String> girlFriends;  
          
        public List<String> getGirlFriends() {  
            return girlFriends;  
        }  
      
        public void setGirlFriends(List<String> girlFriends) {  
            this.girlFriends = girlFriends;  
        }  
      
        public int getAge() {  
            return age;  
        }  
      
        public void setAge(int age) {  
            this.age = age;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public String getName() {  
            return this.name;  
        }  
    }  

    结果是这样:

    <person age="18">  
      <name>pli</name>  
    </person> 
    6. Converter
    Converter这个是属于XStream中的高级特性了,用于基本功能不能满足的情况下让客户自己定制序列化/反系列化的细节,我们还是通过一个例子进行说明。
    假如我要往JAVA bean中添加一个类型为Date的属性:
    @XStreamAlias("person")  
    public class Person {  
        private String name;  
        @XStreamAsAttribute  
        private int age;  
        @XStreamImplicit(itemFieldName="girl")  
        @XStreamOmitField  
        List<String> girlFriends;  
        Date birthday;  
          
        public Date getBirthday() {  
            return birthday;  
        }  
      
        public void setBirthday(Date birthday) {  
            this.birthday = birthday;  
        }  
      
        public List<String> getGirlFriends() {  
            return girlFriends;  
        }  
      
        public void setGirlFriends(List<String> girlFriends) {  
            this.girlFriends = girlFriends;  
        }  
      
        public int getAge() {  
            return age;  
        }  
      
        public void setAge(int age) {  
            this.age = age;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public String getName() {  
            return this.name;  
        }  
    }  

    看看直接序列化的结果:

    <person age="18">  
      <name>pli</name>  
      <birthday>2012-08-04 04:35:01.857 UTC</birthday>  
    </person>

    还不错,但是生日只需要年月日就行了,没必要精确到毫秒,这怎么办呢,只能使用converter,我们这是就需要写代码了。

    public class DateConverter implements Converter {  
        @Override  
        public boolean canConvert(Class clazz) {  
            return (Date.class).equals(clazz);  
        }  
        @Override  
        public void marshal(Object object, HierarchicalStreamWriter writer,  
                MarshallingContext context) {  
            Date date = (Date) object;  
            Calendar calendar = Calendar.getInstance();  
            calendar.setTime(date);  
            SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd");  
            writer.setValue(format.format(calendar.getTime()));  
        }  
        @Override  
        public Object unmarshal(HierarchicalStreamReader arg0,  
                UnmarshallingContext arg1) {  
            return null;  
        }  
    }  
    稍微解释下这段代码:DateConverter 实现了借口Converter,实现了接口中的三个方法:
    • public boolean canConvert(Class clazz) 用来检测本converter是否能够转换输入的类型。
    • public void marshal(Object object, HierarchicalStreamWriter writer,MarshallingContext context) 序列化的方法(JAVA bean --> XML)
    • public Object unmarshal(HierarchicalStreamReader arg0, UnmarshallingContext arg1) 反序列化的方法。因为本例用不到所以没有实现。
    此时我们的JAVA bean也要相应改变:
    @XStreamAlias("person")  
    public class Person {  
        private String name;  
        @XStreamAsAttribute  
        private int age;  
        @XStreamImplicit(itemFieldName="girl")  
        @XStreamOmitField  
        List<String> girlFriends;  
        @XStreamConverter(value=DateConverter.class)  
        Date birthday;  
          
        public Date getBirthday() {  
            return birthday;  
        }  
      
        public void setBirthday(Date birthday) {  
            this.birthday = birthday;  
        }  
      
        public List<String> getGirlFriends() {  
            return girlFriends;  
        }  
      
        public void setGirlFriends(List<String> girlFriends) {  
            this.girlFriends = girlFriends;  
        }  
      
        public int getAge() {  
            return age;  
        }  
      
        public void setAge(int age) {  
            this.age = age;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public String getName() {  
            return this.name;  
        }  
    }  

    看看结果:

    <person age="18">  
      <name>pli</name>  
      <birthday>2012-50-04</birthday>  
    </person>  
    另外在这里简单说说converter的原理:
    其实XStream转换过程就是执行一个个converter的过程,只不过使用的大部分converter都是内建好的,XStream遇到一个待转换的object首先去查找能够转换这个object的转换器(converter)怎么找呢,就是通过converter的canConvert(Class clazz)这个方法,返回为true就是可以转换。明白了吧。
     
    XStream的限制:
    Xstream已经是很不错的东西了,如果真要找不足,我发现有两点。
     
    1. 反序列化的时候无法使用autodetectAnnotations()方法通知XStream对象去识别annotation。
    还记的前面代码中xstream.autodetectAnnotations(true); 吗, 这句代码的意思是告诉XStream对象需要自动识别annotation, 这在序列化(JAVA bean-->XML)的时候没什么问题。但是在反序列化的时候就有问题了,原因官网上说的比较模糊,总之就是不行,只能通过xstream.processAnnotations(Class clazz) 来显式的注册需要使用annotation的类才行,如果JAVA bean很多就会比较麻烦。但一般来说JAVA bean在代码组织结构中都比较集中,如放在听一个package下,这样也好办,可以再程序中将该package下的JAVA bean都获取,然后使用xstream.processAnnotations(Class[] clazzs) 批量注册。
    2. Null 属性无法被序列化。
    之前举的例子JAVA bean中的属性都是被初始化以后才进行序列化的,如果没有初始化就进行序列化会怎样呢 ,还是举个例子
    @XStreamAlias("person")  
    public class Person {  
        private String name = "pli";  
        @XStreamAsAttribute  
        private int age = 19;  
        @XStreamImplicit(itemFieldName="girl")  
        @XStreamOmitField  
        List<String> girlFriends;  
        @XStreamConverter(value=DateConverter.class)  
        Date birthday = new Date();  
          
        public Date getBirthday() {  
            return birthday;  
        }  
      
        public void setBirthday(Date birthday) {  
            this.birthday = birthday;  
        }  
      
        public List<String> getGirlFriends() {  
            return girlFriends;  
        }  
      
        public void setGirlFriends(List<String> girlFriends) {  
            this.girlFriends = girlFriends;  
        }  
      
        public int getAge() {  
            return age;  
        }  
      
        public void setAge(int age) {  
            this.age = age;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public String getName() {  
            return this.name;  
        }  
    }  

    我想将其它属性都进行了初始化但是没有将girlFriends这个属性初始化,即使说girlFriends==null. 序列化以后会怎样呢?

    <person age="18">  
      <name>pli</name>  
      <birthday>2012-36-04</birthday>  
    </person>

    girlFriends这个属性压根就没有被序列化,其实我是想让它序列化成这个样子:

    <person age="18">  
      <name>pli</name>  
      <birthday>2012-36-04</birthday>  
      <girlFriends/>  
    </person>  

    原文:http://blog.csdn.net/pushme_pli/article/details/7829621

  • 相关阅读:
    Python接口自动化测试(15):Django后台设置
    Python接口自动化测试(14):Django安装和项目创建
    Python接口自动化测试(13):Jmeter测试报告
    Python接口自动化测试(12):Jmeter授权设置
    Python接口自动化测试(11):Jmeter数据驱动
    Python接口自动化测试(10):Jmeter参数关联
    Python接口自动化测试(9):Jmeter变量设置
    redis高可用集群搭建
    熔断器 Hystrix
    负载均衡 Ribbon
  • 原文地址:https://www.cnblogs.com/tytr/p/8134006.html
Copyright © 2011-2022 走看看