Knight of the round table

wansong

XStream Annotations 入门【翻译】

1、简单的转换器:

首先创建示例的环境,
下面介绍的是最基础的转换器,首先创建一个Person类:

package com.thoughtworks.xstream.examples;
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

 接着,我们创建一个实例,并转化他:

package com.thoughtworks.xstream.examples;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class PersonTest {
public static void main(String[] args) {
Person person = new Person();
person.setName("Guilherme");
XStream xStream = new XStream(new DomDriver());
System.out.println(xStream.toXML(person));
}
}

 如你所料,得到下面的结果:

<com.thoughtworks.xstream.examples.Person>
<name>Guilherme</name>
</com.thoughtworks.xstream.examples.Person>

下面我们为person类创建一个别名:

XStream xStream = new XStream(new DomDriver());
xStream.alias("person", Person.class);
System.out.println(xStream.toXML(person));

现在的结果就很易读了:

<person>
<name>Guilherme</name>
</person>

到此,我们已经建立好一个可以供我们实验的基础例子了,下面我们来看看XStream的转换器能为我们做些什么:
2,创建一个Person转换器:
下面我们来创建一个简单的转换器,它能:
1,用来转换Person类
2,将Person实例转换成XML
3,将xml转换为Person实例
首先创建一个PersonConverter类,并让这个类实现Converter接口:

package com.thoughtworks.xstream.examples;
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 {
public boolean canConvert(Class clazz) {
return false;
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
return null;
}
}

下面,我们首先告诉转换器,我们只能转换Person类,而不是别的类,包括其子类:

public boolean canConvert(Class clazz) {
return clazz.equals(Person.class);
}

这一步很简单,除非你是用来处理泛型的转换器是会困难一点。

Marshal方法是用来将对象转换为XML的,他有三个参数:
1,我们准备转换的对象
2,我们准备输出对象的writer
3,当前的marshaling context
首先我们将object转换成Person

Person person = (Person) value;
接着,我们就可以开始输出数据了,首先我们创建一个叫做fullname的节点,并将person的名字传给他:

writer.startNode("fullname");
writer.setValue(person.getName());
writer.endNode();

呵呵~很简单吧,

public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Person person = (Person) value;
writer.startNode("fullname");
writer.setValue(person.getName());
writer.endNode();
}

我们可以任意次数的调用start/end node方法,但需要记住,你必须在打开一个节点之后记住关闭它。一般来说,执行转换的操作在setValue方法调用时发生。
下面,我们进入unmarshal方法,我们使用moveDown和moveUp方法在节点树层次中移动,所以,这里我们只需要简单的moveDown,得到值,再moveUp:

Person person = new Person();
reader.moveDown();
person.setName(reader.getValue());
reader.moveUp();

最后,我们得到了一个这样的转换器:

package com.thoughtworks.xstream.examples;
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 {
public boolean canConvert(Class clazz) {
return clazz.equals(Person.class);
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Person person = (Person) value;
writer.startNode("fullname");
writer.setValue(person.getName());
writer.endNode();
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Person person = new Person();
reader.moveDown();
person.setName(reader.getValue());
reader.moveUp();
return person;
}
}

接着,我们在我们的main方法中注册这个转化器:

package com.thoughtworks.xstream.examples;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class PersonTest {
public static void main(String[] args) {
Person person = new Person();
person.setName("Guilherme");
XStream xStream = new XStream(new DomDriver());
xStream.registerConverter(new PersonConverter());
xStream.alias("person", Person.class);
System.out.println(xStream.toXML(person));
}
}

注意到我们怎么注册我们的转换器了么?只需要下面简单的一句:

xStream.registerConverter(new PersonConverter());
最终得到的结果是:

<person>
  <fullname>Guilherme</fullname>
</person>
也许你会说:这只改变了我输出的树,我需要用它来转换数据。
下面我们来尝试在person标签中创建一个叫做fullname的属性,而不是新创建一个节点:
3,一种可选的方式:
首先,为Person创建一个toString方法,里面包含了所有能用来重新创建一个Person实例的数据:

package com.thoughtworks.xstream.examples;
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return getName();
}
}

现在,我们就能把我们的转化器简写为:

 

package com.thoughtworks.xstream.examples;
import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
public class PersonConverter extends AbstractSingleValueConverter {
public boolean canConvert(Class clazz) {
return clazz.equals(Person.class);
}
public Object fromString(String str) {
Person person = new Person();
person.setName(string);
return person;
}
}

现在,输出的XML也会变得更易读(为person创建别名person之后):

<person>Guilherme</person>
名字变成了一个内置的值,而不是一个单独的节点。
4,转换Date:
我们已经知道Converter接口是怎样工作的了,现在我们来创建一个使用Locale对象转换时间的转换器:
在我们的转换器构造方法中,我们将传入一个Locale对象,该Locale对象会作为一个成员属性被转换器持有:

package com.thoughtworks.xstream.examples;
import java.util.Locale;
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;
}
public boolean canConvert(Class clazz) {
return false;
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
return null;
}
}

现在,让我们能转换任何继承了Calendar对象的类:

public boolean canConvert(Class clazz) {
        return Calendar.class.isAssignableFrom(clazz);
}
首先,我们来将Calendar转换成本地化的字符串:首先我们把object转化成Calendar,得到Date对象,并使用DataFormatter来得到一个本地化的时间:

public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Calendar calendar = (Calendar) value;
// grabs the date
Date date = calendar.getTime();
// grabs the formatter
DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
this.locale);
// formats and sets the value
writer.setValue(formatter.format(date));
}

另一方面,在unmarshall方法中,我们创建了一个GregorianCalendar,得到本地化的DataFormat实例,将字符串转换成Date对象,并赋值给GregorianCalendar。

public Object unmarshal(HierarchicalStreamReader reader,
                UnmarshallingContext context) {
        // creates the calendar
        GregorianCalendar calendar = new GregorianCalendar();
        // grabs the converter
        DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
                        this.locale);
        // parses the string and sets the time
        try {
                calendar.setTime(formatter.parse(reader.getValue()));
        } catch (ParseException e) {
                throw new ConversionException(e.getMessage(), e);
        }
        // returns the new object
        return calendar;
}

注意:
1,记住一些DataFormat实现不是线程安全的,所以,不要让你的转换器持有DataFormat的引用
2,在经过了保存和加载的过程后,该转换器可以将其他Calendar实现转换为GregorianCalendar。如果这不是你希望的,只需要修改canConvert方法,并在类型只有为GregorianCalendar的时候再返回true。
现在,我们得到了下面这个转换器:

package com.thoughtworks.xstream.examples;
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;
}
public boolean canConvert(Class clazz) {
return Calendar.class.isAssignableFrom(clazz);
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Calendar calendar = (Calendar) value;
Date date = calendar.getTime();
DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
this.locale);
writer.setValue(formatter.format(date));
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
GregorianCalendar calendar = new GregorianCalendar();
DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
this.locale);
try {
calendar.setTime(formatter.parse(reader.getValue()));
} catch (ParseException e) {
throw new ConversionException(e.getMessage(), e);
}
return calendar;
}
}

现在,我们来测试一下,创建一个main方法:
1,创建一个calendar,
2,创建XStream对象
3,注册该转换器,并使用Brazilian Portuguese本地化对象
4,将对象转化成XML
代码如下:

package com.thoughtworks.xstream.examples;
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 DateTest {
public static void main(String[] args) {
// grabs the current date from the virtual machine
Calendar calendar = new GregorianCalendar();
// creates the xstream
XStream xStream = new XStream(new DomDriver());
// brazilian portuguese locale
xStream.registerConverter(new DateConverter(new Locale("pt", "br")));
// prints the result
System.out.println(xStream.toXML(calendar));
}
}

 可以得到类似如下的结果:

<gregorian-calendar>Sexta-feira, 10 de Fevereiro de 2006</gregorian-calendar>
注意,我们没有为GregorianCalendar创建任何别名,而gregorian-calendar就是默认的名字。
下面我们来试试unmarshal 方法:

// loads the calendar from the string
Calendar loaded = (Calendar) xStream
                .fromXML("<gregorian-calendar>Sexta-feira, 10 de Fevereiro de 2006</gregorian-calendar>");
然后打印出该日期:

// prints using the system defined locale
System.out.println(DateFormat.getDateInstance(DateFormat.SHORT).format(
                loaded.getTime()));
得到的结果为:

2/10/06
5,复杂的转换器:
创建另一个例子:
我们已经创建了两个对象了,现在把它们组合起来:

package com.thoughtworks.xstream.examples;
public class Birthday {
private Person person;
private Calendar date;
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;
}
}

 要转换该类,XStream一点问题都没有。这里,我们实现自己的转换器主要是为了验证,在这里,我们想重用我们刚才的PersonConverter和CalendarConverter。canConvert仍然很简单,不过这里,我们不需要再为每一个属性重新写转换方法了,我们只需要使用已经注册了的转换器来完成转换:

package com.thoughtworks.xstream.examples;
import java.util.Calendar;
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 {
public boolean canConvert(Class clazz) {
return Birthday.class == clazz;
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Birthday birthday = (Birthday)value;
if (value.getPerson() != null) {
writer.startNode("person");
context.convertAnother(value.getPerson());
writer.endNode();
}
if (value.getDate() != null) {
writer.startNode("birth");
context.convertAnother(value.getDate());
writer.endNode();
}
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Birthday birthday = new Birthday();
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("person".equals(reader.getNodeName())) {
Person person = (Person)context.convertAnother(birthday, Person.class);
birthday.setPerson(person);
} else if ("birth".equals(reader.getNodeName())) {
Calendar date = (Calendar)context.convertAnother(birthday, Calendar.class);
birthday.setDate(date);
}
reader.moveUp();
}
return birthday;
}
}

 如果birthday实例能够确保不会出现null值,那么我们就可以去掉marshal和unmarshal方法中对null情况的判断,也不需要循环,而直接根据tag的名字进行解析:

package com.thoughtworks.xstream.examples;
import java.util.Calendar;
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 {
public boolean canConvert(Class clazz) {
return Birthday.class == clazz;
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Birthday birthday = (Birthday)value;
writer.startNode("person");
context.convertAnother(value.getPerson());
writer.endNode();
writer.startNode("birth");
context.convertAnother(value.getDate());
writer.endNode();
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Birthday birthday = new Birthday();
reader.moveDown();
Person person = (Person)context.convertAnother(birthday, Person.class);
birthday.setPerson(person);
reader.moveUp();
reader.moveDown();
Calendar date = (Calendar)context.convertAnother(birthday, Calendar.class);
birthday.setDate(date);
reader.moveUp();
return birthday;
}
}

posted on 2010-09-07 21:24 w@ns0ng 阅读(510) 评论(0)  编辑  收藏 所属分类: java


只有注册用户登录后才能发表评论。


网站导航: