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

来源:互联网 发布:软件毕业设计开题报告 编辑:程序博客网 时间:2024/05/29 09:12

【我在项目组使用的是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:
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;@XStreamAsAttributeprivate 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;@XStreamAsAttributeprivate 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;@XStreamAsAttributeprivate 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;@XStreamAsAttributeprivate int age;@XStreamImplicit(itemFieldName="girl")@XStreamOmitFieldList<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;@XStreamAsAttributeprivate int age;@XStreamImplicit(itemFieldName="girl")@XStreamOmitFieldList<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 {@Overridepublic boolean canConvert(Class clazz) {return (Date.class).equals(clazz);}@Overridepublic 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()));}@Overridepublic 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;@XStreamAsAttributeprivate int age;@XStreamImplicit(itemFieldName="girl")@XStreamOmitFieldList<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就是可以转换。明白了吧。

二、JavaBean和XML互转【非注解方式
准备的JavaBean如下:
Person javabean类
public class Person {private String firstname;private String lastname;private PhoneNumber phone;private PhoneNumber fax;public Person() {super();}public Person(String firstname, String lastname) {super();this.firstname = firstname;this.lastname = lastname;}public String getFirstname() {return firstname;}public void setFirstname(String firstname) {this.firstname = firstname;}public String getLastname() {return lastname;}public void setLastname(String lastname) {this.lastname = lastname;}public PhoneNumber getPhone() {return phone;}public void setPhone(PhoneNumber phone) {this.phone = phone;}public PhoneNumber getFax() {return fax;}public void setFax(PhoneNumber fax) {this.fax = fax;}@Overridepublic String toString() {return "Person [firstname=" + firstname + ", lastname=" + lastname+ ", phone=" + phone + ", fax=" + fax + "]";}}
PhoneNumber javabean类
public class PhoneNumber {private int code;private String number;public PhoneNumber() {super();}public PhoneNumber(int code, String number) {super();this.code = code;this.number = number;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getNumber() {return number;}public void setNumber(String number) {this.number = number;}@Overridepublic String toString() {return "PhoneNumber [ code =" + code + ", number=" + number + "]";}
转换测试类
1、将JavaBean转换为XML字符串
public class ConvertTest {        /**      * 将Person转换成xml字符串      */      @Test      public void personToXML() {          XStream xstream = new XStream();          xstream.alias("person", Person.class);          xstream.alias("phonenumber", PhoneNumber.class);                Person joe = new Person("Joe", "Walnes");          joe.setPhone(new PhoneNumber(123, "1234-456"));          joe.setFax(new PhoneNumber(123, "9999-999"));            String xml = xstream.toXML(joe);          System.out.println(xml);      }  }
xstream.alias("person", Person.class);  
xstream.alias("phonenumber", PhoneNumber.class);  
这两句是给对象加上别名,等价于注解方式里面的 @XStreamAlias("person")标签。

输出结果为:
<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>

2、将xml字符串转换成JavaBean
@Test      public void xmlToPerson() {          String site = "CAN";          XStream xstream = new XStream();          xstream.alias("person", Person.class);          xstream.alias("phonenumber", PhoneNumber.class);            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>";          xstream.omitField(Person.class, site);          Person newJoe = (Person) xstream.fromXML(xml);          System.out.println(newJoe.toString());      }  


三、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中的属性都是被初始化以后才进行序列化的,如果没有初始化就进行序列化会怎样呢 ,还是举个例子
@XStreamAlias("person")public class Person {private String name = "pli";@XStreamAsAttributeprivate int age = 19;@XStreamImplicit(itemFieldName="girl")@XStreamOmitFieldList<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>
有什么办法没,真没啥办法。我查了查源码,确实如果某个属性为null的话就不进行序列化的,唯一的办法是修改源码,这个太费事。
另外提一点,XStream也提供了不使用annotation的方式,有兴趣请在XStream的官网上查看。

四、xmlUtils--【xml和javaBean互转工具类】
package com.openeap.webservice.base;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.util.Date;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.openeap.modules.taskBoard.entity.TaskBoardSjToQd;import com.thoughtworks.xstream.XStream;import com.thoughtworks.xstream.io.xml.DomDriver;public class XmlUtil {private static Logger log = LoggerFactory.getLogger(XmlUtil.class);/** * java 转换成xml *  * @Title: toXml * @Description: TODO * @param obj *            对象实例 * @return String xml字符串 */public static String toXml(Object obj) {XStream xstream = new XStream();// XStream xstream=new XStream(new DomDriver()); //直接用jaxp dom来解释// XStream xstream=new XStream(new DomDriver("utf-8"));// //指定编码解析器,直接用jaxp dom来解释// //如果没有这句,xml中的根元素会是<包.类名>;或者说:注解根本就没生效,所以的元素名就是类的属性xstream.processAnnotations(obj.getClass()); // 通过注解方式的,一定要有这句话return xstream.toXML(obj);}/** * 将传入xml文本转换成Java对象 *  * @Title: toBean * @Description: TODO * @param xmlStr * @param cls *            xml对应的class类 * @return T xml对应的class类的实例对象 *  *         调用的方法实例:PersonBean person=XmlUtil.toBean(xmlStr, *         PersonBean.class); */public static <T> T toBean(String xmlStr, Class<T> cls) {// 注意:不是new Xstream(); 否则报错:java.lang.NoClassDefFoundError:// org/xmlpull/v1/XmlPullParserFactoryXStream xstream = new XStream(new DomDriver());xstream.alias(cls.getSimpleName(), cls);//xstream.processAnnotations(cls);T obj = (T) xstream.fromXML(xmlStr);return obj;}/** * 写到xml文件中去 *  * @Title: writeXMLFile * @Description: TODO * @param obj *            对象 * @param absPath *            绝对路径 * @param fileName *            文件名 * @return boolean */public static boolean toXMLFile(Object obj, String absPath, String fileName) {String strXml = toXml(obj);String filePath = absPath + fileName;File file = new File(filePath);if (!file.exists()) {try {file.createNewFile();} catch (IOException e) {log.error("创建{" + filePath + "}文件失败!!!");return false;}}// end ifOutputStream ous = null;try {ous = new FileOutputStream(file);ous.write(strXml.getBytes());ous.flush();} catch (Exception e1) {log.error("写{" + filePath + "}文件失败!!!");return false;} finally {if (ous != null)try {ous.close();} catch (IOException e) {log.error("写{" + filePath + "}文件关闭输出流异常!!!");}}return true;}/** * 从xml文件读取报文 *  * @Title: toBeanFromFile * @Description: TODO * @param absPath *            绝对路径 * @param fileName *            文件名 * @param cls * @throws Exception * @return T */public static <T> T toBeanFromFile(String filePath, Class<T>[] cls) throws Exception {//String filePath = absPath + fileName;InputStream ins = null;try {ins = new FileInputStream(new File(filePath));} catch (Exception e) {throw new Exception("读{" + filePath + "}文件失败!", e);}String encode = "UTF-8";// useEncode(cls);XStream xstream = new XStream(new DomDriver(encode));xstream.processAnnotations(cls);/*for(Class tempCls : cls){xstream.alias(tempCls.getSimpleName(), tempCls);}*/T obj = null;try {obj = (T) xstream.fromXML(ins);} catch (Exception e) {// TODO Auto-generated catch blockthrow new Exception("解析{" + filePath + "}文件失败!", e);}if (ins != null)ins.close();return obj;}public static void main(String[] args) {// TODO Auto-generated method stub}}

















0 0
原创粉丝点击