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>
有什么办法没,真没啥办法。我查了查源码,确实如果某个属性为null的话就不进行序列化的,唯一的办法是修改源码,这个太费事,如果你有兴趣请参看这个链接上的文章,会有帮助: 点击打开链接
另外提一点,XStream也提供了不适用annotation的方式,有兴趣请在XStream的官网上查看。
好了,写的够多了,都比较浅显,有什么疑问请告之。






















  • 22
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
使用XStream库实现XML与对象之间的互转可以通过以下步骤完成: 1. 引入XStream库的依赖:在你的项目中引入XStream库的依赖,可以参考我之前提供的Maven和Gradle配置。 2. 定义Java类:创建一个Java类来表示你的对象,并使用XStream提供的注解来指定XML元素和属性与Java类的映射关系。例如: ```java import com.thoughtworks.xstream.annotations.XStreamAlias; @XStreamAlias("person") public class Person { private String name; private int age; // 省略构造函数、getter和setter方法 // ... } ``` 3. 将对象换为XML:使用XStreamJava对象换为XML字符串。示例如下: ```java import com.thoughtworks.xstream.XStream; public class Main { public static void main(String[] args) { // 创建XStream实例 XStream xStream = new XStream(); // 设置类别名,用于与XML元素进行映射 xStream.alias("person", Person.class); // 创建一个Person对象 Person person = new Person(); person.setName("John"); person.setAge(30); // 将Person对象换为XML字符串 String xml = xStream.toXML(person); // 输出XML字符串 System.out.println(xml); } } ``` 以上代码将输出以下XML字符串: ```xml <person> <name>John</name> <age>30</age> </person> ``` 4. 将XML换为对象:使用XStreamXML字符串换回Java对象。示例如下: ```java import com.thoughtworks.xstream.XStream; public class Main { public static void main(String[] args) { // 创建XStream实例 XStream xStream = new XStream(); // 设置类别名,用于与XML元素进行映射 xStream.alias("person", Person.class); // XML字符串 String xml = "<person><name>John</name><age>30</age></person>"; // 将XML换为Person对象 Person person = (Person) xStream.fromXML(xml); // 输出Person对象的属性 System.out.println("Name: " + person.getName()); System.out.println("Age: " + person.getAge()); } } ``` 以上代码将输出: ``` Name: John Age: 30 ``` 通过以上步骤,你可以使用XStream实现XML与对象之间的互转。你可以根据具体的需求对Java类和XML结构进行调整和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值