最近在项目中遇到了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:
  1. package xstreamTest;
  2. public class Person {
  3. private String name;
  4. private int age;
  5. public int getAge() {
  6. return age;
  7. }
  8. public void setAge(int age) {
  9. this.age = age;
  10. }
  11. public void setName(String name) {
  12. this.name = name;
  13. }
  14. public String getName() {
  15. return this.name;
  16. }
  17. }

转换代码是这样的:

  1. XStream xstream = new XStream();
  2. Person person = new Person();
  3. person.setName("pli");
  4. person.setAge(18);
  5. System.out.println(xstream.toXML(person));

我们得到了这样的结果:

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

而执行代码会变成这样:

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

好玩吧。

4. 处理List
如果JAVA bean中有List是什么情形呢。
  1. @XStreamAlias("person")
  2. public class Person {
  3. private String name;
  4. @XStreamAsAttribute
  5. private int age;
  6. List<String> girlFriends;
  7. public List<String> getGirlFriends() {
  8. return girlFriends;
  9. }
  10. public void setGirlFriends(List<String> girlFriends) {
  11. this.girlFriends = girlFriends;
  12. }
  13. public int getAge() {
  14. return age;
  15. }
  16. public void setAge(int age) {
  17. this.age = age;
  18. }
  19. public void setName(String name) {
  20. this.name = name;
  21. }
  22. public String getName() {
  23. return this.name;
  24. }
  25. }
直接转换我们会得到这样的结果:
  1. <person age="18">
  2. <name>pli</name>
  3. <girlFriends>
  4. <string>YuanYuanGao</string>
  5. <string>QiShu</string>
  6. <string>BoZhiZhang</string>
  7. </girlFriends>
  8. </person>


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

  1. @XStreamAlias("person")
  2. public class Person {
  3. private String name;
  4. @XStreamAsAttribute
  5. private int age;
  6. @XStreamImplicit(itemFieldName="girl")
  7. List<String> girlFriends;
  8. public List<String> getGirlFriends() {
  9. return girlFriends;
  10. }
  11. public void setGirlFriends(List<String> girlFriends) {
  12. this.girlFriends = girlFriends;
  13. }
  14. public int getAge() {
  15. return age;
  16. }
  17. public void setAge(int age) {
  18. this.age = age;
  19. }
  20. public void setName(String name) {
  21. this.name = name;
  22. }
  23. public String getName() {
  24. return this.name;
  25. }
  26. }
结果是这样:
  1. <person age="18">
  2. <name>pli</name>
  3. <girl>YuanYuanGao</girl>
  4. <girl>QiShu</girl>
  5. <girl>BoZhiZhang</girl>
  6. </person>

5. 忽略属性

如果在JAVA bean中有些属性不想被序列化,XStream提供了解决这个需求的annotation: @XStreamOmitField
比如说不想讲girlfriends这个List序列化
  1. @XStreamAlias("person")
  2. public class Person {
  3. private String name;
  4. @XStreamAsAttribute
  5. private int age;
  6. @XStreamImplicit(itemFieldName="girl")
  7. @XStreamOmitField
  8. List<String> girlFriends;
  9. public List<String> getGirlFriends() {
  10. return girlFriends;
  11. }
  12. public void setGirlFriends(List<String> girlFriends) {
  13. this.girlFriends = girlFriends;
  14. }
  15. public int getAge() {
  16. return age;
  17. }
  18. public void setAge(int age) {
  19. this.age = age;
  20. }
  21. public void setName(String name) {
  22. this.name = name;
  23. }
  24. public String getName() {
  25. return this.name;
  26. }
  27. }
结果是这样:
  1. <person age="18">
  2. <name>pli</name>
  3. </person>
6. Converter
Converter这个是属于XStream中的高级特性了,用于基本功能不能满足的情况下让客户自己定制序列化/反系列化的细节,我们还是通过一个例子进行说明。
假如我要往JAVA bean中添加一个类型为Date的属性:
  1. @XStreamAlias("person")
  2. public class Person {
  3. private String name;
  4. @XStreamAsAttribute
  5. private int age;
  6. @XStreamImplicit(itemFieldName="girl")
  7. @XStreamOmitField
  8. List<String> girlFriends;
  9. Date birthday;
  10. public Date getBirthday() {
  11. return birthday;
  12. }
  13. public void setBirthday(Date birthday) {
  14. this.birthday = birthday;
  15. }
  16. public List<String> getGirlFriends() {
  17. return girlFriends;
  18. }
  19. public void setGirlFriends(List<String> girlFriends) {
  20. this.girlFriends = girlFriends;
  21. }
  22. public int getAge() {
  23. return age;
  24. }
  25. public void setAge(int age) {
  26. this.age = age;
  27. }
  28. public void setName(String name) {
  29. this.name = name;
  30. }
  31. public String getName() {
  32. return this.name;
  33. }
  34. }
看看直接序列化的结果:
  1. <person age="18">
  2. <name>pli</name>
  3. <birthday>2012-08-04 04:35:01.857 UTC</birthday>
  4. </person>
还不错,但是生日只需要年月日就行了,没必要精确到毫秒,这怎么办呢,只能使用converter,我们这是就需要写代码了。
  1. public class DateConverter implements Converter {
  2. @Override
  3. public boolean canConvert(Class clazz) {
  4. return (Date.class).equals(clazz);
  5. }
  6. @Override
  7. public void marshal(Object object, HierarchicalStreamWriter writer,
  8. MarshallingContext context) {
  9. Date date = (Date) object;
  10. Calendar calendar = Calendar.getInstance();
  11. calendar.setTime(date);
  12. SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd");
  13. writer.setValue(format.format(calendar.getTime()));
  14. }
  15. @Override
  16. public Object unmarshal(HierarchicalStreamReader arg0,
  17. UnmarshallingContext arg1) {
  18. return null;
  19. }
  20. }
稍微解释下这段代码: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也要相应改变:
  1. @XStreamAlias("person")
  2. public class Person {
  3. private String name;
  4. @XStreamAsAttribute
  5. private int age;
  6. @XStreamImplicit(itemFieldName="girl")
  7. @XStreamOmitField
  8. List<String> girlFriends;
  9. @XStreamConverter(value=DateConverter.class)
  10. Date birthday;
  11. public Date getBirthday() {
  12. return birthday;
  13. }
  14. public void setBirthday(Date birthday) {
  15. this.birthday = birthday;
  16. }
  17. public List<String> getGirlFriends() {
  18. return girlFriends;
  19. }
  20. public void setGirlFriends(List<String> girlFriends) {
  21. this.girlFriends = girlFriends;
  22. }
  23. public int getAge() {
  24. return age;
  25. }
  26. public void setAge(int age) {
  27. this.age = age;
  28. }
  29. public void setName(String name) {
  30. this.name = name;
  31. }
  32. public String getName() {
  33. return this.name;
  34. }
  35. }
看看结果:
  1. <person age="18">
  2. <name>pli</name>
  3. <birthday>2012-50-04</birthday>
  4. </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中的属性都是被初始化以后才进行序列化的,如果没有初始化就进行序列化会怎样呢 ,还是举个例子
  1. @XStreamAlias("person")
  2. public class Person {
  3. private String name = "pli";
  4. @XStreamAsAttribute
  5. private int age = 19;
  6. @XStreamImplicit(itemFieldName="girl")
  7. @XStreamOmitField
  8. List<String> girlFriends;
  9. @XStreamConverter(value=DateConverter.class)
  10. Date birthday = new Date();
  11. public Date getBirthday() {
  12. return birthday;
  13. }
  14. public void setBirthday(Date birthday) {
  15. this.birthday = birthday;
  16. }
  17. public List<String> getGirlFriends() {
  18. return girlFriends;
  19. }
  20. public void setGirlFriends(List<String> girlFriends) {
  21. this.girlFriends = girlFriends;
  22. }
  23. public int getAge() {
  24. return age;
  25. }
  26. public void setAge(int age) {
  27. this.age = age;
  28. }
  29. public void setName(String name) {
  30. this.name = name;
  31. }
  32. public String getName() {
  33. return this.name;
  34. }
  35. }
我想将其它属性都进行了初始化但是没有将girlFriends这个属性初始化,即使说girlFriends==null. 序列化以后会怎样呢?
  1. <person age="18">
  2. <name>pli</name>
  3. <birthday>2012-36-04</birthday>
  4. </person>

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

  1. <person age="18">
  2. <name>pli</name>
  3. <birthday>2012-36-04</birthday>
  4. <girlFriends/>
  5. </person>
有什么办法没,真没啥办法。我查了查源码,确实如果某个属性为null的话就不进行序列化的,唯一的办法是修改源码,这个太费事,如果你有兴趣请参看这个链接上的文章,会有帮助:点击打开链接
另外提一点,XStream也提供了不适用annotation的方式,有兴趣请
在XStream的官网上查看。

点击打开链接

转自 http://blog.csdn.net/pushme_pli/article/details/7829621

JAVA bean与XML互转的利器---XStream的更多相关文章

  1. JAVA Bean和XML之间的相互转换 - XStream简单入门

    JAVA Bean和XML之间的相互转换 - XStream简单入门 背景介绍 XStream的简介 注解简介 应用实例 背景介绍 我们在工作中经常 遇到文件解析为数据或者数据转化为xml文件的情况, ...

  2. JAXB java类与xml互转

    JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术.该过程中,JAXB也提供了将XML实例文档反向 ...

  3. Java bean和json互转时,屏蔽某个属性

    有的时候我们把java bean 转换成json的时候,希望屏蔽掉某个属性,这时可以在java bean的属性上加上@JsonIgnore注解,在com.fasterxml.jackson.annot ...

  4. java bean 转换工具

    考量要素: 1.简单的约定优于配置的同名属性copy 2.嵌套属性copy 3.flattern(扁平化)支持,要支持N层结构的copy到一层结构. 4.性能 如下这个网页,里面提到了好多工具. ht ...

  5. 使用JAXB实现Bean与Xml相互转换

    最近几天,我自己负责的应用这边引入了一个新的合作方,主要是我这边调用他们的接口,但是有个很坑的地方,他们传参居然不支持json格式,并且只支持xml格式进行交互,于是自己写了一个工具类去支持bean与 ...

  6. Java JSON、XML文件/字符串与Bean对象互转解析

    前言      在做web或者其他项目中,JSON与XML格式的数据是大家经常会碰见的2种.在与各种平台做数据对接的时候,JSON与XML格式也是基本的数据传递格式,本文主要简单的介绍JSON/XML ...

  7. Spring基础篇——通过Java注解和XML配置装配bean

    自动化装配的确有很大的便利性,但是却并不能适用在所有的应用场景,比如需要装配的组件类不是由自己的应用程序维护,而是引用了第三方的类库,这个时候自动装配便无法实现,Spring对此也提供了相应的解决方案 ...

  8. Java Jaxb JavaBean与XML互转

    1.Jaxb - Java Arcitecture for XML Binding 是业界的一个标准,是一项能够依据XML Schema产生Java类的技术. Jaxb2.0是Jdk1.6的组成部分. ...

  9. Spring基础篇——通过Java注解和XML配置装配bean(转载)

      作者:陈本布衣 出处:http://www.cnblogs.com/chenbenbuyi 本文版权归作者和博客园共有,欢迎转载分享,但必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留 ...

随机推荐

  1. Mysql_存储功能

    先上一段代码: -->DELIMETER;     ----加上这一句:DELIMETER的作用是设定客户机的分隔符,表示用//包含的是一段程序,一起执行,而不是见到“:”就执行 结束的时候写上 ...

  2. Java多线程编程总结(学习博客)

    Java多线程编程总结:网址:http://lavasoft.blog.51cto.com/62575/27069/

  3. java星座、年龄、日期等

    星座: public static String getStar(Date date) { Calendar cal = Calendar.getInstance(); cal.setTime(dat ...

  4. 【html】【16】高级篇--毛玻璃效果[模糊]

    参考: http://www.zhangxinxu.com/wordpress/2013/11/%E5%B0%8Ftip-%E4%BD%BF%E7%94%A8css%E5%B0%86%E5%9B%BE ...

  5. 【mysql】【分组】后取每组的top2

    DROP TABLE IF EXISTS `tb1`; CREATE TABLE `tb1` ( `id` ) NOT NULL AUTO_INCREMENT, `a` ) DEFAULT NULL, ...

  6. jQuery 源码分析4: jQuery.extend

    jQuery.extend是jQuery最重要的方法之一,下面看看jQuery是怎样实现扩展操作的 // 如果传入一个对象,这个对象的属性会被添加到jQuery对象中 // 如果传入两个或多个对象,所 ...

  7. wait(...) notify() notifyAll()

    简介 wait.notify.notifyAll是Java中3个与线程有关的方法,它们都是Object类中的方法. 其中,wait方法有3个重载形式: 1.wait() 2.wait(long tim ...

  8. 配置php的CAS客户端

    1.下载安装xmapp 2.开启Apache服务. 3.下载php的CAS客户端源码包(我使用的是CAS-1.2.0.tgz),解压到xmap的htdocs目录下(D:\xmapp\htdocs),进 ...

  9. Json概述以及python对json的相关操作《转》

    什么是json: JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.易于人阅读和编写.同时也易于机器解析和生成.它基于JavaScript Programm ...

  10. jQuery—一些常见方法(3)【width(),innerWidth(),outerWidth()】

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...