1,问题:
有的时候,过多的调用XStream的别名(aliases)和转化器(converter)方法来定制您的XStream会是很难看的,你或许会希望使用java annotations来配置POJO,该入门指南介绍XStream提供的一些用于配置XStream的annotations。首先来看一个Message类:
package com.thoughtworks.xstream;
package com.thoughtworks.xstream;
public class RendezvousMessage {
private int messageType;
public RendezvousMessage(int messageType) {
this.messageType = messageType;
}
}
接着使用XStream来创建我们的XML文件:
package com.thoughtworks.xstream;
public class Tutorial {
public static void main(String[] args) {
XStream stream = new XStream(new DomDriver());
RendezvousMessage msg = new RendezvousMessage(15);
System.out.println(stream.toXML(msg));
}
}
我们得到类似于下面的XML结果:
<com.thoughtworks.xstream.RendezvousMessage>
<messageType>15</messageType>
</com.thoughtworks.xstream.RendezvousMessage>
别名annotations:
最常用的annotation是为类型和属性创建别名的标签:@XStreamAlias。下面是使用该标签注释我们的类型和属性:
@XStreamAlias("message")
class RendezvousMessage {
@XStreamAlias("type")
private int messageType;
public RendezvousMessage(int messageType) {
this.messageType = messageType;
}
}
下面的操作过程比较奇特,我们需要告诉XStream去从这个类型中读出annotation。这个操作和annotation本身的机制有关,你必须告诉XStream主动去解析annotation:
public static void main(String[] args) {
XStream stream = new XStream(new DomDriver());
Annotations.configureAliases(stream, RendezvousMessage.class);
RendezvousMessage msg = new RendezvousMessage(15);
System.out.println(stream.toXML(msg));
}
注意我们调用了Annotation类的configureAliases静态方法。这个方法首先把XStream中的所有的别名annotations注册到第一个参数中,这个方法使用的是可变参数方法,可以一次性的注册多个类型。下面是我们期望的输出:
<message>
<type>15</type>
</message>
3,去掉collections标记:
下面我们为RendezvousMessage添加一个List类型属性content,并使用annotations来实现implicit collections相同的功能:
@XStreamAlias("message")
class RendezvousMessage {
@XStreamAlias("type")
private int messageType;
private List<String> content;
public RendezvousMessage(int messageType, String ... content) {
this.messageType = messageType;
this.content = Arrays.asList(content);
}
}
public static void main(String[] args) {
XStream stream = new XStream(new DomDriver());
Annotations.configureAliases(stream, RendezvousMessage.class);
RendezvousMessage msg = new RendezvousMessage(15, "firstPart","secondPart");
System.out.println(stream.toXML(msg));
}
输出的结果:
<message>
<type>15</type>
<content class="java.util.Arrays$ArrayList">
<a class="string-array">
<string>firstPart</string>
<string>secondPart</string>
</a>
</content>
</message>
这并不是我们想要的结果,现在面我们就使用标签来取消content列表的collection属性名:
@XStreamAlias("message")
class RendezvousMessage {
@XStreamAlias("type")
private int messageType;
@XStreamImplicit
private List<String> content;
public RendezvousMessage(int messageType, String... content) {
this.messageType = messageType;
this.content = Arrays.asList(content);
}
}
下面是输出的结果:
<message>
<type>15</type>
<a class="string-array">
<string>firstPart</string>
<string>secondPart</string>
</a>
</message>
已经接近了,下一步,希望移除a标记,并使用part来标记各个content。为了到达这个目的,我们使用lmplicit collections标签的另一个属性,这个属性创建了一个collection中每一个数据的tag名字:
@XStreamAlias("message")
class RendezvousMessage {
@XStreamAlias("type")
private int messageType;
@XStreamImplicit(itemFieldName="part")
private List<String> content;
public RendezvousMessage(int messageType, String... content) {
this.messageType = messageType;
this.content = Arrays.asList(content);
}
}
我们得到下面的结果:
<message>
<type>15</type>
<part>firstPart</part>
<part>secondPart</part>
</message>
5,自定义转换器:
下面我们创建另一个属性,用来定义一个message创建的timestamp:
@XStreamAlias("message")
class RendezvousMessage {
@XStreamAlias("type")
private int messageType;
@XStreamImplicit(itemFieldName="part")
private List<String> content;
private Calendar created = new GregorianCalendar();
public RendezvousMessage(int messageType, String... content) {
this.messageType = messageType;
this.content = Arrays.asList(content);
}
}
得到下面的xml:
<message>
<type>15</type>
<part>firstPart</part>
<part>secondPart</part>
<created>
<time>1154097812245</time>
<timezone>America/Sao_Paulo</timezone>
</created>
</message>
下面我们面临这样一个问题:我们想为这个Calendar使用一个自定义的converter。其实这很简单,使用一个自定义converter的annotation来标记该属性:
@XStreamAlias("message")
class RendezvousMessage {
@XStreamAlias("type")
private int messageType;
@XStreamImplicit(itemFieldName="part")
private List<String> content;
@XStreamConverter(SingleValueCalendarConverter.class)
private Calendar created = new GregorianCalendar();
public RendezvousMessage(int messageType, String... content) {
this.messageType = messageType;
this.content = Arrays.asList(content);
}
}
下面来创建这个conventer:
public class SingleValueCalendarConverter implements Converter {
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
Calendar calendar = (Calendar) source;
writer.setValue(String.valueOf(calendar.getTime().getTime()));
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(new Date(Long.parseLong(reader.getValue())));
return calendar;
}
public boolean canConvert(Class type) {
return type.equals(GregorianCalendar.class);
}
}
接着就可以得到下面这个输出了:
<message>
<type>15</type>
<part>firstPart</part>
<part>secondPart</part>
<created>1154097812245</created>
</message>
注意,@XStreamConverter标签是自动读取的,不需要调用任何Annotations上的静态方法。XStream能简单的检查运行环境是否支持Java1.5。
如果需要type属性作为message标签的一个属性的话:
<message type="15">
<part>firstPart</part>
<part>secondPart</part>
<created>1154097812245</created>
</message>
你只需要添加一个XStreamAsAttribute annotation即可:
@XStreamAlias("message")
class RendezvousMessage {
@XStreamAlias("type")
@XStreamAsAttribute
private int messageType;
@XStreamImplicit(itemFieldName="part")
private List<String> content;
@XStreamConverter(SingleValueCalendarConverter.class)
private Calendar created = new GregorianCalendar();
public RendezvousMessage(int messageType, String... content) {
this.messageType = messageType;
this.content = Arrays.asList(content);
}
}