七、转换器(Converter)
我们程序中的POJO是千变万化的,而且需求也是千奇百怪的,所以XStream中的内置的转换器的功能不一定能够满足我们的要求,所以我们就需要自己构建转换器。
1,一个基本的转换器
有如下代码:
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class XStreamTest4 {
public static void main(String[] args) {
Person person = new Person();
person.setName("张三");
XStream xstream = new XStream(new DomDriver());
xstream.alias("person", Person.class);
System.out.println(xstream.toXML(person));
}
}
class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return getName();
}
}
运行结果是:
<person>
<name>张三</name>
</person>
如果我们需要输出如下信息:
<person>
<fullname>张三</fullname>
</person>
该怎么办?
当然,我们可以使用XStream默认的转换器
xstream.aliasField("fullname", Person.class,"name");
,甚至我们可以直接使用注解@XStreamAlias。但是这不是我们要介绍的,我们需要创建自己的转换器PersonConverter。
PersonConverter需要有3个功能:
a)告诉XStream对象,它能够转换Person类的对象(canConvert方法)
b)能够将Person对象转换为XML(marshal方法)
c)能够将XML转换成为Person对象(unmarshal方法)
现在,我们实现第一个功能(canConvert方法):
//告诉XStream对象,它能够转换Person类的对象
public boolean canConvert(Class clazz) {
return clazz.equals(Person.class);
}
就这么简单!
然后,我们实现第二个功能(marshal方法):
//能够将Person对象转换为XML
public void marshal(
Object value, //我们将要转换的对象,这里是Person对象
HierarchicalStreamWriter writer,//用于输出XML结果的writer
MarshallingContext context //序列化环境上下文
) {
//写入顺序
//1,强制转换为我们我们需要的类型
Person person = (Person) value;
//2,开始写入fullname节点,相当于写入<fullname>
writer.startNode("fullname");
//3,给fullname节点赋值
writer.setValue(person.getName());
//4,结束fullname节点,相当于写入</fullname>
writer.endNode();
// //如果你愿意,顺便也可以写一点其他的东西
// writer.startNode("otherContent");
// writer.setValue("这是一大串其他内容,你可以根据自己的需要写内容!");
// writer.endNode();
}
最后,我们实现第三个功能(unmarshal方法):
//能够将XML转换成为Person对象
public Object unmarshal(
HierarchicalStreamReader reader,//用于读取XML的reader
UnmarshallingContext context //反序列化环境上下文
) {
//1,先创建一个Person对象
Person person = new Person();
//2,判断<person>节点下还有没有其他可以读取的节点
while(reader.hasMoreChildren()){
//3,开始读取下一个(也可能是第一个)节点,选择当前<person>节点的子节点作为当前节点
reader.moveDown();
//4,获取当前节点的值
String value = reader.getValue();
if("fullname".equals(reader.getNodeName())){
person.setName(value);
}
//输出当前节点的内容
System.out.println("node="+reader.getNodeName()+";value="+value);
//5,返回上一层节点<person>
reader.moveUp();
}
return person;
}
于是我们的PersonConverter就是:
package cn.tjpu.zhw.xml.xstream4;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class PersonConverter implements Converter {
//告诉XStream对象,它能够转换Person类的对象
public boolean canConvert(Class clazz) {
return clazz.equals(Person.class);
}
//能够将Person对象转换为XML
public void marshal(
Object value, //我们将要转换的对象,这里是Person对象
HierarchicalStreamWriter writer,//用于输出XML结果的writer
MarshallingContext context //序列化环境上下文
) {
//写入顺序
//1,强制转换为我们我们需要的类型
Person person = (Person) value;
//2,开始写入fullname节点,相当于写入<fullname>
writer.startNode("fullname");
//3,给fullname节点赋值
writer.setValue(person.getName());
//4,结束fullname节点,相当于写入</fullname>
writer.endNode();
// //如果你愿意,顺便也可以写一点其他的东西
// writer.startNode("otherContent");
// writer.setValue("这是一大串其他内容,你可以根据自己的需要写内容!");
// writer.endNode();
}
//能够将XML转换成为Person对象
public Object unmarshal(
HierarchicalStreamReader reader,//用于读取XML的reader
UnmarshallingContext context //反序列化环境上下文
) {
//1,先创建一个Person对象
Person person = new Person();
//2,判断<person>节点下还有没有其他可以读取的节点
while(reader.hasMoreChildren()){
//3,开始读取下一个(也可能是第一个)节点,选择当前<person>节点的子节点作为当前节点
reader.moveDown();
//4,获取当前节点的值
String value = reader.getValue();
if("fullname".equals(reader.getNodeName())){
person.setName(value);
}
//输出当前节点的内容
System.out.println("node="+reader.getNodeName()+";value="+value);
//5,返回上一层节点<person>
reader.moveUp();
}
return person;
}
}
同时我们修改main方法文件:
package cn.tjpu.zhw.xml.xstream4;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class XStreamTest4 {
public static void main(String[] args) {
//创建Person对象
Person person = new Person();
person.setName("张三");
//创建XStream对象
XStream xstream = new XStream(new DomDriver());
xstream.alias("person", Person.class);
//注册PersonConverter转换器
xstream.registerConverter(new PersonConverter());
//使用marshal方法,将Person对象转换成为XML
System.out.println("****使用marshal方法,将Person对象转换成为XML==>");
String xml = xstream.toXML(person);
System.out.println(xml);
//使用unmarshal方法,将XML转换成为Person对象
System.out.println();
System.out.println("****使用unmarshal方法,将XML转换成为Person对象==>");
Person p = (Person)xstream.fromXML(xml);
//输出Person对象
System.out.println();
System.out.println("****输出Person对象==>");
System.out.println(p);
}
}
class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Person对象的name="+getName();
}
}
运行结果如下:
****使用marshal方法,将Person对象转换成为XML==>
<person>
<fullname>张三</fullname>
</person>
****使用unmarshal方法,将XML转换成为Person对象==>
node=fullname;value=张三
****输出Person对象==>
Person对象的name=张三
我们成功了!!!!!!
2,另一个简单的转换器
现在我们需要程序输出如下:
<person>张三</person>
该咋办?
我们再创建一个转换器:
package cn.tjpu.zhw.xml.xstream4;
import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
public class AnotherPersonConverter extends AbstractSingleValueConverter {
// 告诉XStream对象,它能够转换Person类的对象
public boolean canConvert(Class clazz) {
return clazz.equals(Person.class);
}
// 将Person对象转换为XML,参数obj就是要转换的Person对象,返回值就是<person>节点的值
public String toString(Object obj) {
Person p = (Person) obj;
return p.getName();
}
// 将XML转换成为Person对象,参数str就是<person>节点的值,返回值就是转换得到的Person对象
public Object fromString(String str) {
Person person = new Person();
person.setName(str);
System.out.println("参数str="+str);
return person;
}
}
修改Main方法:
public class XStreamTest4 {
public static void main(String[] args) {
//创建Person对象
Person person = new Person();
person.setName("张三");
//创建XStream对象
XStream xstream = new XStream(new DomDriver());
xstream.alias("person", Person.class);
// //注册PersonConverter转换器
// xstream.registerConverter(new PersonConverter());
//注册AnotherPersonConverter转换器
xstream.registerConverter(new AnotherPersonConverter());
//使用toString方法,将Person对象转换成为XML
System.out.println("****使用toString方法,将Person对象转换成为XML==>");
String xml = xstream.toXML(person);
System.out.println(xml);
//使用fromString方法,将XML转换成为Person对象
System.out.println();
System.out.println("****使用fromString方法,将XML转换成为Person对象==>");
Person p = (Person)xstream.fromXML(xml);
//输出Person对象
System.out.println();
System.out.println("****输出Person对象==>");
System.out.println(p);
}
}
运行结果:
****使用toString方法,将Person对象转换成为XML==>
<person>张三</person>
****使用fromString方法,将XML转换成为Person对象==>
参数str=张三
****输出Person对象==>
Person对象的name=张三
这正是我们预期的结果!!!
3,时间转换器
通过上面两个例子我们知道了Converter接口的简单工作方式,现在我们创建一个新的时间转换器DateConverter,这个时间转换器将以Local对象作为构造方法的参数。
这个时间转换器同样有类似PersonConverter转换器的功能:
a)告诉XStream对象,它能够转换Calendar类的对象(canConvert方法)
b)能够将Calendar对象转换为XML(marshal方法)
c)能够将XML转换成为Calendar对象(unmarshal方法)
现在实现第一个功能:
// 告诉XStream对象,它能够转换Calendar类及其所有子类定义的对象
public boolean canConvert(Class clazz) {
return Calendar.class.isAssignableFrom(clazz);
}
然后,实现第二个功能:
// 能够将Calendar对象转换为XML
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
//value是将要转换的Calendar对象
Calendar calendar = (Calendar) value;
Date date = calendar.getTime();
//设定格式化格式
DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
this.locale);
//格式化并输出Calendar对象
writer.setValue(formatter.format(date));
}
最后,实现最后一个功能:
// 能够将XML转换成为Calendar对象
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
//创建一个Calendar对象
GregorianCalendar calendar = new GregorianCalendar();
//设定解析格式
DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
this.locale);
try {
//解析指定格式的Calendar对象
calendar.setTime(formatter.parse(reader.getValue()));
} catch (ParseException e) {
throw new ConversionException(e.getMessage(), e);
}
//返回解析成功的Calendar对象
return calendar;
}
于是,我们的DateConverter定义如下:
package cn.tjpu.zhw.xml.xstream4;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class DateConverter implements Converter {
private Locale locale;
public DateConverter(Locale locale) {
super();
this.locale = locale;
}
// 告诉XStream对象,它能够转换Calendar类及其所有子类定义的对象
public boolean canConvert(Class clazz) {
return Calendar.class.isAssignableFrom(clazz);
}
// 能够将Calendar对象转换为XML
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
//value是将要转换的Calendar对象
Calendar calendar = (Calendar) value;
Date date = calendar.getTime();
//设定格式化格式
DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
this.locale);
//格式化并输出Calendar对象
writer.setValue(formatter.format(date));
}
// 能够将XML转换成为Calendar对象
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
//创建一个Calendar对象
GregorianCalendar calendar = new GregorianCalendar();
//设定解析格式
DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
this.locale);
try {
//解析指定格式的Calendar对象
calendar.setTime(formatter.parse(reader.getValue()));
} catch (ParseException e) {
throw new ConversionException(e.getMessage(), e);
}
//返回解析成功的Calendar对象
return calendar;
}
}
再写我们的main方法:
package cn.tjpu.zhw.xml.xstream4;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class DateMain {
public static void main(String[] args) {
// 获取当前时间
Calendar calendar = new GregorianCalendar();
// 创建XStream对象
XStream xstream = new XStream(new DomDriver());
xstream.alias("date", Calendar.class);
// 注册我们定义的时间转换器
xstream.registerConverter(new DateConverter(new Locale("zh", "cn")));
// 转换并输出XML
String xml = xstream.toXML(calendar);
System.out.println(xstream.toXML(calendar));
// 加载XML中的时间数据
Calendar loaded = (Calendar) xstream.fromXML(xml);
//格式化并输出时间
System.out.println(DateFormat.getDateInstance(DateFormat.MEDIUM).format(
loaded.getTime()));
}
}
运行,结果如下:
<date>2013年12月24日 星期二</date>
2013-12-24
4,更加复杂的转换器
复杂的转换器都是由一系列基本的转换器组成的,我们只需要将这些基本的转换器按照适当的顺序排列组合就行了。
下面有一个复杂的Birthday类:
package cn.tjpu.zhw.xml.xstream4;
import java.util.Calendar;
public class Birthday {
private Person person;
private Calendar date;
private char gender;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public Calendar getDate() {
return date;
}
public void setDate(Calendar date) {
this.date = date;
}
public char getGender() {
return gender;
}
public void setGenderMale() {
this.gender = 'm';
}
public void setGenderFemale() {
this.gender = 'f';
}
public String toString(){
return "{person="+person+"};{date="+date.getTime()+"};{gender="+gender+"}";
}
}
我们将怎样转换这个类呢?
答案是,我们只需要将现有的转换器组合成一个BirthdayConverter转换器就成了!
package cn.tjpu.zhw.xml.xstream4;
import java.util.Calendar;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class BirthdayConverter implements Converter {
// 告诉XStream对象,它能够转换Birthday类的对象
public boolean canConvert(Class clazz) {
return Birthday.class == clazz;
}
// 能够将Birthday对象转换为XML
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
//value是将要被转换的Birthday对象
Birthday birthday = (Birthday) value;
//给<birthday>节点添加gender属性
if (birthday.getGender() != '\0') {
writer.addAttribute("gender",
Character.toString(birthday.getGender()));
}
//给<birthday>节点添加子节点<person>
if (birthday.getPerson() != null) {
writer.startNode("person");
context.convertAnother(birthday.getPerson());
writer.endNode();
}
//给<birthday>节点添加子节点<birth>
if (birthday.getDate() != null) {
writer.startNode("birth");
context.convertAnother(birthday.getDate());
writer.endNode();
}
}
// 能够将XML转换成为Birthday对象
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
//创建Birthday对象
Birthday birthday = new Birthday();
//当前节点是<birthday>
//解析<birthday>节点的gender属性
String gender = reader.getAttribute("gender");
if (gender != null) {
if (gender.length() > 0) {
if (gender.charAt(0) == 'f') {
birthday.setGenderFemale();
} else if (gender.charAt(0) == 'm') {
birthday.setGenderMale();
} else {
throw new ConversionException("Invalid gender value: "
+ gender);
}
} else {
throw new ConversionException(
"Empty string is invalid gender value");
}
}
//遍历解析<birthday>节点的所有子节点
while (reader.hasMoreChildren()) {
//将下一个(也可能是第一个)子节点作为当前节点
reader.moveDown();
//解析<person>节点
if ("person".equals(reader.getNodeName())) {
Person person = (Person) context.convertAnother(birthday,
Person.class);
birthday.setPerson(person);
}
//解析<birth>节点
else if ("birth".equals(reader.getNodeName())) {
Calendar date = (Calendar) context.convertAnother(birthday,
Calendar.class);
birthday.setDate(date);
}
//返回到<birthday>节点作为当前节点
reader.moveUp();
}
//返回解析得到的Birthday对象
return birthday;
}
}
下面写main方法:
package cn.tjpu.zhw.xml.xstream4;
import java.util.GregorianCalendar;
import com.thoughtworks.xstream.XStream;
public class BirthdayMain {
public static void main(String[] args) {
//创建Birthday对象
Birthday birthday = new Birthday();
Person p = new Person();
p.setName("张三");
birthday.setPerson(p);
birthday.setDate(new GregorianCalendar());
birthday.setGenderMale();
//创建XStream对象
XStream xstream = new XStream();
xstream.alias("birthday", Birthday.class);
//注册BirthdayConverter转换器
xstream.registerConverter(new BirthdayConverter());
//将Birthday对象转换成为XML并输出
String xml = xstream.toXML(birthday);
System.out.println("**************将Birthday对象转换成为XML并输出**************");
System.out.println(xml);
//将XML转换成为Birthday对象
Birthday b = (Birthday)xstream.fromXML(xml);
//输出Birthday对象
System.out.println();
System.out.println("**************将XML转换成为Birthday对象**************");
System.out.println(b);
}
}
运行结果:
**************将Birthday对象转换成为XML并输出**************
<birthday gender="m">
<person>
<name>张三</name>
</person>
<birth>
<time>1387897906531</time>
<timezone>Asia/Shanghai</timezone>
</birth>
</birthday>
**************将XML转换成为Birthday对象**************
{person=Person对象的name=张三};{date=Tue Dec 24 23:11:46 CST 2013};{gender=m}