XML和JavaBean之间的互转---XStream

前端之家收集整理的这篇文章主要介绍了XML和JavaBean之间的互转---XStream前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

【我在项目组使用的是xstream-1.4.3.jar】

最近在项目中遇到了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就可以了。
我准备在例子中体现以下功能
  • 基本转换
  • 对象起别名
  • 处理属性
  • 处理List
  • 忽略field
一、JavaBean和XML互转【注解方式
1. 基本转换
这是一个普通的JAVA bean:
  1. package xstreamTest;
  2. public class Person {
  3. private String name;
  4. private int age;
  5.  
  6. public int getAge() {
  7. return age;
  8. }
  9.  
  10. public void setAge(int age) {
  11. this.age = age;
  12. }
  13.  
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17.  
  18. public String getName() {
  19. return this.name;
  20. }
  21. }
转换代码是这样的:

  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.  
  7. public int getAge() {
  8. return age;
  9. }
  10.  
  11. public void setAge(int age) {
  12. this.age = age;
  13. }
  14.  
  15. public void setName(String name) {
  16. this.name = name;
  17. }
  18.  
  19. public String getName() {
  20. return this.name;
  21. }
  22. }
而执行代码会变成这样:

  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.  
  10. public void setAge(int age) {
  11. this.age = age;
  12. }
  13.  
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17.  
  18. public String getName() {
  19. return this.name;
  20. }
  21. }
结果是这样的:

  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.  
  11. public void setGirlFriends(List<String> girlFriends) {
  12. this.girlFriends = girlFriends;
  13. }
  14.  
  15. public int getAge() {
  16. return age;
  17. }
  18.  
  19. public void setAge(int age) {
  20. this.age = age;
  21. }
  22.  
  23. public void setName(String name) {
  24. this.name = name;
  25. }
  26.  
  27. public String getName() {
  28. return this.name;
  29. }
  30. }
直接转换我们会得到这样的结果:

  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.  
  12. public void setGirlFriends(List<String> girlFriends) {
  13. this.girlFriends = girlFriends;
  14. }
  15.  
  16. public int getAge() {
  17. return age;
  18. }
  19.  
  20. public void setAge(int age) {
  21. this.age = age;
  22. }
  23.  
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27.  
  28. public String getName() {
  29. return this.name;
  30. }
  31. }
结果是这样:

  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.  
  13. public void setGirlFriends(List<String> girlFriends) {
  14. this.girlFriends = girlFriends;
  15. }
  16.  
  17. public int getAge() {
  18. return age;
  19. }
  20.  
  21. public void setAge(int age) {
  22. this.age = age;
  23. }
  24.  
  25. public void setName(String name) {
  26. this.name = name;
  27. }
  28.  
  29. public String getName() {
  30. return this.name;
  31. }
  32. }
结果是这样:
  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.  
  14. public void setBirthday(Date birthday) {
  15. this.birthday = birthday;
  16. }
  17.  
  18. public List<String> getGirlFriends() {
  19. return girlFriends;
  20. }
  21.  
  22. public void setGirlFriends(List<String> girlFriends) {
  23. this.girlFriends = girlFriends;
  24. }
  25.  
  26. public int getAge() {
  27. return age;
  28. }
  29.  
  30. public void setAge(int age) {
  31. this.age = age;
  32. }
  33.  
  34. public void setName(String name) {
  35. this.name = name;
  36. }
  37.  
  38. public String getName() {
  39. return this.name;
  40. }
  41. }
看看直接序列化的结果:
  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,MarshallingContext context) {
  8. Date date = (Date) object;
  9. Calendar calendar = Calendar.getInstance();
  10. calendar.setTime(date);
  11. SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd");
  12. writer.setValue(format.format(calendar.getTime()));
  13. }
  14. @Override
  15. public Object unmarshal(HierarchicalStreamReader arg0,UnmarshallingContext arg1) {
  16. return null;
  17. }
  18. }
稍微解释下这段代码:DateConverter 实现了接口Converter,实现了接口中的三个方法
  • public boolean canConvert(Class clazz) 用来检测本converter是否能够转换输入的类型。
  • public void marshal(Object object,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.  
  15. public void setBirthday(Date birthday) {
  16. this.birthday = birthday;
  17. }
  18.  
  19. public List<String> getGirlFriends() {
  20. return girlFriends;
  21. }
  22.  
  23. public void setGirlFriends(List<String> girlFriends) {
  24. this.girlFriends = girlFriends;
  25. }
  26.  
  27. public int getAge() {
  28. return age;
  29. }
  30.  
  31. public void setAge(int age) {
  32. this.age = age;
  33. }
  34.  
  35. public void setName(String name) {
  36. this.name = name;
  37. }
  38.  
  39. public String getName() {
  40. return this.name;
  41. }
  42. }
看看结果:
  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就是可以转换。明白了吧。

二、JavaBean和XML互转【非 注解方式
准备的JavaBean如下:
Person javabean类
  1. public class Person {
  2. private String firstname;
  3. private String lastname;
  4. private PhoneNumber phone;
  5. private PhoneNumber fax;
  6.  
  7. public Person() {
  8. super();
  9. }
  10.  
  11. public Person(String firstname,String lastname) {
  12. super();
  13. this.firstname = firstname;
  14. this.lastname = lastname;
  15. }
  16.  
  17. public String getFirstname() {
  18. return firstname;
  19. }
  20.  
  21. public void setFirstname(String firstname) {
  22. this.firstname = firstname;
  23. }
  24.  
  25. public String getLastname() {
  26. return lastname;
  27. }
  28.  
  29. public void setLastname(String lastname) {
  30. this.lastname = lastname;
  31. }
  32.  
  33. public PhoneNumber getPhone() {
  34. return phone;
  35. }
  36.  
  37. public void setPhone(PhoneNumber phone) {
  38. this.phone = phone;
  39. }
  40.  
  41. public PhoneNumber getFax() {
  42. return fax;
  43. }
  44.  
  45. public void setFax(PhoneNumber fax) {
  46. this.fax = fax;
  47. }
  48.  
  49. @Override
  50. public String toString() {
  51. return "Person [firstname=" + firstname + ",lastname=" + lastname
  52. + ",phone=" + phone + ",fax=" + fax + "]";
  53. }
  54.  
  55. }
PhoneNumber javabean类
  1. public class PhoneNumber {
  2. private int code;
  3. private String number;
  4.  
  5. public PhoneNumber() {
  6. super();
  7. }
  8.  
  9. public PhoneNumber(int code,String number) {
  10. super();
  11. this.code = code;
  12. this.number = number;
  13. }
  14.  
  15. public int getCode() {
  16. return code;
  17. }
  18.  
  19. public void setCode(int code) {
  20. this.code = code;
  21. }
  22.  
  23. public String getNumber() {
  24. return number;
  25. }
  26.  
  27. public void setNumber(String number) {
  28. this.number = number;
  29. }
  30.  
  31. @Override
  32. public String toString() {
  33. return "PhoneNumber [ code =" + code + ",number=" + number + "]";
  34. }
转换测试类
1、将JavaBean转换为XML字符串
  1. public class ConvertTest {
  2. /**
  3. * 将Person转换成xml字符串
  4. */
  5. @Test
  6. public void personToXML() {
  7. XStream xstream = new XStream();
  8. xstream.alias("person",Person.class);
  9. xstream.alias("phonenumber",PhoneNumber.class);
  10. Person joe = new Person("Joe","Walnes");
  11. joe.setPhone(new PhoneNumber(123,"1234-456"));
  12. joe.setFax(new PhoneNumber(123,"9999-999"));
  13. String xml = xstream.toXML(joe);
  14. System.out.println(xml);
  15. }
  16. }
xstream.alias("person",Person.class);
xstream.alias("phonenumber",PhoneNumber.class);
这两句是给对象加上别名,等价于注解方式里面的@XStreamAlias("person")标签

输出结果为:
  1. <person>
  2. <firstname>Joe</firstname>
  3. <lastname>Walnes</lastname>
  4. <phone>
  5. <code>123</code>
  6. <number>1234-456</number>
  7. </phone>
  8. <fax>
  9. <code>123</code>
  10. <number>9999-999</number>
  11. </fax>
  12. </person>
2、将xml字符串转换成JavaBean
  1. @Test
  2. public void xmlToPerson() {
  3. String site = "CAN";
  4. XStream xstream = new XStream();
  5. xstream.alias("person",PhoneNumber.class);
  6. String xml = "<person><firstname>Joe</firstname><lastname>Walnes</lastname><phone><code>123</code><number>1234-456</number></phone><fax><code>123</code><number>9999-999</number></fax></person>";
  7. xstream.omitField(Person.class,site);
  8. Person newJoe = (Person) xstream.fromXML(xml);
  9. System.out.println(newJoe.toString());
  10. }


三、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 属性无法被序列化,即 值为空的字段是不会输出到XML的。
之前举的例子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.  
  15. public void setBirthday(Date birthday) {
  16. this.birthday = birthday;
  17. }
  18.  
  19. public List<String> getGirlFriends() {
  20. return girlFriends;
  21. }
  22.  
  23. public void setGirlFriends(List<String> girlFriends) {
  24. this.girlFriends = girlFriends;
  25. }
  26.  
  27. public int getAge() {
  28. return age;
  29. }
  30.  
  31. public void setAge(int age) {
  32. this.age = age;
  33. }
  34.  
  35. public void setName(String name) {
  36. this.name = name;
  37. }
  38.  
  39. public String getName() {
  40. return this.name;
  41. }
  42. }
我想将其它属性都进行了初始化但是没有将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的官网上查看。

四、xmlUtils--【xml和javaBean互转工具类】
  1. package com.openeap.webservice.base;
  2.  
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.io.OutputStream;
  9. import java.util.Date;
  10.  
  11. import org.slf4j.Logger;
  12. import org.slf4j.LoggerFactory;
  13.  
  14. import com.openeap.modules.taskBoard.entity.TaskBoardSjToQd;
  15. import com.thoughtworks.xstream.XStream;
  16. import com.thoughtworks.xstream.io.xml.DomDriver;
  17.  
  18. public class XmlUtil {
  19. private static Logger log = LoggerFactory.getLogger(XmlUtil.class);
  20.  
  21. /**
  22. * java 转换成xml
  23. *
  24. * @Title: toXml
  25. * @Description: TODO
  26. * @param obj
  27. * 对象实例
  28. * @return String xml字符串
  29. */
  30. public static String toXml(Object obj) {
  31. XStream xstream = new XStream();
  32. // XStream xstream=new XStream(new DomDriver()); //直接用jaxp dom来解释
  33. // XStream xstream=new XStream(new DomDriver("utf-8"));
  34. // //指定编码解析器,直接用jaxp dom来解释
  35.  
  36. // //如果没有这句,xml中的根元素会是<包.类名>;或者说:注解根本就没生效,所以的元素名就是类的属性
  37. xstream.processAnnotations(obj.getClass()); // 通过注解方式的,一定要有这句话
  38. return xstream.toXML(obj);
  39. }
  40.  
  41. /**
  42. * 将传入xml文本转换成Java对象
  43. *
  44. * @Title: toBean
  45. * @Description: TODO
  46. * @param xmlStr
  47. * @param cls
  48. * xml对应的class类
  49. * @return T xml对应的class类的实例对象
  50. *
  51. * 调用方法实例:PersonBean person=XmlUtil.toBean(xmlStr,* PersonBean.class);
  52. */
  53. public static <T> T toBean(String xmlStr,Class<T> cls) {
  54. // 注意:不是new Xstream(); 否则报错:java.lang.NoClassDefFoundError:
  55. // org/xmlpull/v1/XmlPullParserFactory
  56. XStream xstream = new XStream(new DomDriver());
  57. xstream.alias(cls.getSimpleName(),cls);
  58. //xstream.processAnnotations(cls);
  59. T obj = (T) xstream.fromXML(xmlStr);
  60. return obj;
  61. }
  62.  
  63. /**
  64. * 写到xml文件中去
  65. *
  66. * @Title: writeXMLFile
  67. * @Description: TODO
  68. * @param obj
  69. * 对象
  70. * @param absPath
  71. * 绝对路径
  72. * @param fileName
  73. * 文件
  74. * @return boolean
  75. */
  76.  
  77. public static boolean toXMLFile(Object obj,String absPath,String fileName) {
  78. String strXml = toXml(obj);
  79. String filePath = absPath + fileName;
  80. File file = new File(filePath);
  81. if (!file.exists()) {
  82. try {
  83. file.createNewFile();
  84. } catch (IOException e) {
  85. log.error("创建{" + filePath + "}文件失败!!!");
  86. return false;
  87. }
  88. }// end if
  89. OutputStream ous = null;
  90. try {
  91. ous = new FileOutputStream(file);
  92. ous.write(strXml.getBytes());
  93. ous.flush();
  94. } catch (Exception e1) {
  95. log.error("写{" + filePath + "}文件失败!!!");
  96. return false;
  97. } finally {
  98. if (ous != null)
  99. try {
  100. ous.close();
  101. } catch (IOException e) {
  102. log.error("写{" + filePath + "}文件关闭输出流异常!!!");
  103. }
  104. }
  105. return true;
  106. }
  107.  
  108. /**
  109. * 从xml文件读取报文
  110. *
  111. * @Title: toBeanFromFile
  112. * @Description: TODO
  113. * @param absPath
  114. * 绝对路径
  115. * @param fileName
  116. * 文件
  117. * @param cls
  118. * @throws Exception
  119. * @return T
  120. */
  121. public static <T> T toBeanFromFile(String filePath,Class<T>[] cls) throws Exception {
  122. //String filePath = absPath + fileName;
  123. InputStream ins = null;
  124. try {
  125. ins = new FileInputStream(new File(filePath));
  126. } catch (Exception e) {
  127. throw new Exception("读{" + filePath + "}文件失败!",e);
  128. }
  129.  
  130. String encode = "UTF-8";// useEncode(cls);
  131. XStream xstream = new XStream(new DomDriver(encode));
  132. xstream.processAnnotations(cls);
  133. /*for(Class tempCls : cls){
  134. xstream.alias(tempCls.getSimpleName(),tempCls);
  135. }*/
  136. T obj = null;
  137. try {
  138. obj = (T) xstream.fromXML(ins);
  139. } catch (Exception e) {
  140. // TODO Auto-generated catch block
  141. throw new Exception("解析{" + filePath + "}文件失败!",e);
  142. }
  143. if (ins != null)
  144. ins.close();
  145. return obj;
  146. }
  147.  
  148. public static void main(String[] args) {
  149. // TODO Auto-generated method stub
  150.  
  151.  
  152. }
  153. }

猜你在找的XML相关文章