使用XStream是实现XML与Java对象的转换(3)--注解

六、使用注解(Annotation)

总是使用XStream对象的别名方法和注册转换器,会让人感到非常的乏味,又会产生很多重复性代码,于是我们可以使用注解的方式来配置要序列化的POJO对象。

1,最基本的注解:类的别名性注解和字段的别名性注解(XStreamAlias)

有这样一段代码:

Java代码  
  1. import com.thoughtworks.xstream.XStream;  
  2.    
  3. public class XStreamTest3 {  
  4.    public static void main(String[] args) {  
  5.       XStream stream = new XStream();  
  6.       RendezvousMessage msg = new RendezvousMessage(15);  
  7.       System.out.println(stream.toXML(msg));  
  8.    }  
  9.    
  10. }  
  11. class RendezvousMessage {  
  12.     
  13.    private int messageType;  
  14.     
  15.    public RendezvousMessage(int messageType) {  
  16.       this.messageType = messageType;  
  17.    }  
  18. }  

运行结果是:

Java代码  
  1. <cn.tjpu.zhw.xml.RendezvousMessage>  
  2.   <messageType>15</messageType>  
  3. </cn.tjpu.zhw.xml.RendezvousMessage>  

如果我们需要将输出的XML文本是这样:

Java代码  
  1. <message>  
  2.   <type>15</type>  
  3. </message>  

该怎么办?

我们当然可以在main方法中调用XStream对象的别名映射方法进行处理,但我们也可以使用更简单的注解的方式进行处理。

对RendezvousMessage类的定义进行注解如下:

Java代码  
  1. //对类的别名性注解  
  2. @XStreamAlias("message")  
  3. class RendezvousMessage {  
  4.     
  5.    //对字段的别名性注解  
  6.    @XStreamAlias("type")  
  7.    private int messageType;  
  8.     
  9.    public RendezvousMessage(int messageType) {  
  10.       this.messageType = messageType;  
  11.    }  
  12. }  

但是,我们进行注解之后发现输出的结果并没有改变,为什么?

因为XStream对象默认是不读取和识别注解的,需要我们主动提醒它,而后XStream对象才能在转换的的时候读取注解。

更改main方法如下:

Java代码  
  1. public static void main(String[] args) {  
  2.       XStream stream = new XStream();  
  3.       //通知XStream对象读取并识别RendezvousMessage中的注解  
  4.       stream.processAnnotations(RendezvousMessage.class);  
  5.       RendezvousMessage msg = new RendezvousMessage(15);  
  6.       System.out.println(stream.toXML(msg));  
  7. }  

这样输出的结果就能与预想的一样了。

注意:当使用XStream对象处理一个被注解的类型时,XStream对象也会处理所有与其相关的类型的注解信息,即该类型的父类、父接口、所有子类的注解。

processAnnotations方法还有一个重载的方法,是以Class []作为参数的。

2,隐式集合注解(XStreamImplicit)

现在我们给RendezvousMessage类添加一个List集合字段,并且更改一下RendezvousMessage的构造方法,新的代码如下:

Java代码  
  1. import java.util.Arrays;  
  2. import java.util.List;  
  3.    
  4. import com.thoughtworks.xstream.XStream;  
  5. import com.thoughtworks.xstream.annotations.XStreamAlias;  
  6.    
  7. public class XStreamTest3 {  
  8.    public static void main(String[] args) {  
  9.       XStream stream = new XStream();  
  10.       //通知XStream对象读取并识别RendezvousMessage中的注解  
  11.       stream.processAnnotations(RendezvousMessage.class);  
  12.       RendezvousMessage msg = new RendezvousMessage(15,"first","second");  
  13.       System.out.println(stream.toXML(msg));  
  14.    }  
  15.    
  16. }  
  17.    
  18. //对类的别名性注解  
  19. @XStreamAlias("message")  
  20. class RendezvousMessage {  
  21.     
  22.    //对字段的别名性注解  
  23.    @XStreamAlias("type")  
  24.    private int messageType;  
  25.     
  26.    //新添加的集合字段  
  27.    private List<String> content;  
  28.     
  29.    //经改造的构造方法  
  30.    public RendezvousMessage(int messageType, String ... content) {  
  31.       this.messageType = messageType;  
  32.       this.content = Arrays.asList(content);  
  33.    }  
  34. }  

运输出结果如下:

Java代码  
  1. <message>  
  2.   <type>15</type>  
  3.   <content class="java.util.Arrays$ArrayList">  
  4.     <a class="string-array">  
  5.       <string>first</string>  
  6.       <string>second</string>  
  7.     </a>  
  8.   </content>  
  9. </message>  

但是,如果我们想让输出的XML格式如下:

Java代码  
  1. <message>  
  2.   <type>15</type>  
  3.   <part>firstPart</part>  
  4.   <part>secondPart</part>  
  5. </message>  

该怎么办?

现在我们给集合字段添加隐式集合性注解,以去除集合的根节点:

Java代码  
  1. //对类的别名性注解  
  2. @XStreamAlias("message")  
  3. class RendezvousMessage {  
  4.     
  5.    //对字段的别名性注解  
  6.    @XStreamAlias("type")  
  7.    private int messageType;  
  8.     
  9.    //隐式集合性注解  
  10.    @XStreamImplicit  
  11.    private List<String> content;  
  12.     
  13.    public RendezvousMessage(int messageType, String ... content) {  
  14.       this.messageType = messageType;  
  15.       this.content = Arrays.asList(content);  
  16.    }  
  17. }  

重新运行程序,输出结果如下:

Java代码  
  1. <message>  
  2.   <type>15</type>  
  3.   <string>first</string>  
  4.   <string>second</string>  
  5. </message>  

输出的结果中,集合的每一个子节点的节点名都是string,现在需要将子节点的节点名改为part,这样就需要继续更改注解项:

Java代码  
  1. //对类的别名性注解  
  2. @XStreamAlias("message")  
  3. class RendezvousMessage {  
  4.     
  5.    //对字段的别名性注解  
  6.    @XStreamAlias("type")  
  7.    private int messageType;  
  8.     
  9.    //对隐式集合的注解,将每一个子节点的节点名都改为part  
  10.    @XStreamImplicit(itemFieldName="part")  
  11.    private List<String> content;  
  12.     
  13.    public RendezvousMessage(int messageType, String ... content) {  
  14.       this.messageType = messageType;  
  15.       this.content = Arrays.asList(content);  
  16.    }  
  17. }  

这样输出的结果就能够跟预想的一样了,成功了!!!

注意:隐式集合注解同样可以用于数组和Map对象。

3,注解转换器(XStreamConverter)

现在我们再给RendezvousMessage类添加两个字段,一个boolean字段和一个时间Calendar字段,代码如下:

Java代码  
  1. public class XStreamTest3 {  
  2.    public static void main(String[] args) {  
  3.       XStream stream = new XStream();  
  4.       // 通知XStream对象读取并识别RendezvousMessage中的注解  
  5.       stream.processAnnotations(RendezvousMessage.class);  
  6.       RendezvousMessage msg = new RendezvousMessage(15,false,"first","second");  
  7.       System.out.println(stream.toXML(msg));  
  8.    }  
  9.    
  10. }  
  11.    
  12. // 对类的别名性注解  
  13. @XStreamAlias("message")  
  14. class RendezvousMessage {  
  15.    
  16.    // 对字段的别名性注解  
  17.    @XStreamAlias("type")  
  18.    private int messageType;  
  19.    
  20.    // 隐式集合性注解  
  21.    @XStreamImplicit(itemFieldName = "part")  
  22.    private List<String> content;  
  23.    
  24.    private boolean important;  
  25.    
  26.    private Calendar created = new GregorianCalendar();  
  27.    
  28.    // 再次对构造方法进行了改造  
  29.    public RendezvousMessage(int messageType, boolean important,  
  30.         String... content) {  
  31.       this.messageType = messageType;  
  32.       this.important = important;  
  33.       this.content = Arrays.asList(content);  
  34.    }  
  35.    
  36. }  

运行结果如下:

Java代码  
  1. <message>  
  2.   <type>15</type>  
  3.   <part>first</part>  
  4.   <part>second</part>  
  5.   <important>false</important>  
  6.   <created>  
  7.     <time>1387534087343</time>  
  8.     <timezone>Asia/Shanghai</timezone>  
  9.   </created>  
  10. </message>  

现在,我们要将输出结果改造为:

Java代码  
  1. <message>  
  2.   <type>15</type>  
  3.   <part>firstPart</part>  
  4.   <part>secondPart</part>  
  5.   <important>no</important>  
  6.   <created>1379430873703</created>  
  7. </message>  

该如何做?

首先,我们使用注解处理Calendar时间字段的转换,先定义一个时间的转换器SingleValueCalendarConverter,代码如下:

Java代码  
  1. package cn.tjpu.zhw.xml;  
  2.    
  3. import java.util.Calendar;  
  4. import java.util.Date;  
  5. import java.util.GregorianCalendar;  
  6.    
  7. import com.thoughtworks.xstream.converters.Converter;  
  8. import com.thoughtworks.xstream.converters.MarshallingContext;  
  9. import com.thoughtworks.xstream.converters.UnmarshallingContext;  
  10. import com.thoughtworks.xstream.io.HierarchicalStreamReader;  
  11. import com.thoughtworks.xstream.io.HierarchicalStreamWriter;  
  12.    
  13. //必须是public类型  
  14. public class SingleValueCalendarConverter implements Converter {  
  15.    
  16.     public void marshal(Object source, HierarchicalStreamWriter writer,  
  17.             MarshallingContext context) {  
  18.         Calendar calendar = (Calendar) source;  
  19.         writer.setValue(String.valueOf(calendar.getTime().getTime()));  
  20.     }  
  21.    
  22.     public Object unmarshal(HierarchicalStreamReader reader,  
  23.             UnmarshallingContext context) {  
  24.         GregorianCalendar calendar = new GregorianCalendar();  
  25.         calendar.setTime(new Date(Long.parseLong(reader.getValue())));  
  26.         return calendar;  
  27.     }  
  28.    
  29.     public boolean canConvert(Class type) {  
  30.         return type.equals(GregorianCalendar.class);  
  31.     }  
  32. }  

然后,我们需要使用SingleValueCalendarConverter转换器对Calendar字段进行注解:

Java代码  
  1. //对类的别名性注解  
  2. @XStreamAlias("message")  
  3. class RendezvousMessage {  
  4.     
  5.    //对字段的别名性注解  
  6.    @XStreamAlias("type")  
  7.    private int messageType;  
  8.     
  9.    //对隐式集合的注解,将每一个子节点的节点名都改为part  
  10.    @XStreamImplicit(itemFieldName="part")  
  11.    private List<String> content;  
  12.     
  13.    private boolean important;  
  14.     
  15.    //为该字段的注解指定转换器  
  16.    @XStreamConverter(SingleValueCalendarConverter.class)  
  17.    private Calendar created = new GregorianCalendar();  
  18.     
  19.    public RendezvousMessage(int messageType, String ... content) {  
  20.       this.messageType = messageType;  
  21.       this.content = Arrays.asList(content);  
  22.    }  
  23. }  

运行结果如下:

Java代码  
  1. <message>  
  2.   <type>15</type>  
  3.   <part>first</part>  
  4.   <part>second</part>  
  5.   <important>false</important>  
  6.   <created>1387534774062</created>  
  7. </message>  

但是我们发现important节点中的内容是true或false,怎样让它变成yes或no呢?

我们可以使用框架为我们提供的一个转换器BooleanConverter

修改RendezvousMessage的类定义:

Java代码  
  1. //对类别名的注解  
  2. @XStreamAlias("message")  
  3. class RendezvousMessage {  
  4.     
  5.    //对字段别名的注解  
  6.    @XStreamAlias("type")  
  7.    private int messageType;  
  8.     
  9.    //对隐式集合的注解,将每一个子节点的节点名都改为part  
  10.    @XStreamImplicit(itemFieldName="part")  
  11.    private List<String> content;  
  12.     
  13.    //将true/false改为yes/no  
  14.    @XStreamConverter(value=BooleanConverter.class, booleans={false}, strings={"yes", "no"})  
  15.    private boolean important;  
  16.     
  17.    //为该字段添加转换器注解  
  18.    @XStreamConverter(SingleValueCalendarConverter.class)  
  19.    private Calendar created = new GregorianCalendar();  
  20.     
  21.    public RendezvousMessage(int messageType, boolean important, String... content) {  
  22.       this.messageType = messageType;  
  23.       this.important = important;  
  24.       this.content = Arrays.asList(content);  
  25.    }  
  26. }  

运行结果如下:

Java代码  
  1. <message>  
  2.   <type>15</type>  
  3.   <part>first</part>  
  4.   <part>second</part>  
  5.   <important>no</important>  
  6.   <created>1387534827609</created>  
  7. </message>  

这正是我们想要的!!!!

4,属性注解

现在我们想将上面的XML格式改造成为:

Java代码  
  1. <message type="15" important="no">  
  2.   <part>firstPart</part>  
  3.   <part>secondPart</part>  
  4.   <created>1154097812245</created>  
  5. </message>  

,也就是把type节点和important节点作为父节点的属性,该怎么做?

答案是,使用属性注解:

@XStreamAsAttribute

代码如下:

Java代码  
  1. public class XStreamTest3 {  
  2.    public static void main(String[] args) {  
  3.       XStream stream = new XStream();  
  4.       // 通知XStream对象读取并识别RendezvousMessage中的注解  
  5.       stream.processAnnotations(RendezvousMessage.class);  
  6.       RendezvousMessage msg = new RendezvousMessage(15, false, "first",  
  7.            "second");  
  8.       System.out.println(stream.toXML(msg));  
  9.    }  
  10.    
  11. }  
  12.    
  13. // 对类的别名性注解  
  14. @XStreamAlias("message")  
  15. class RendezvousMessage {  
  16.    
  17.    //将type节点变成属性  
  18.    @XStreamAsAttribute  
  19.    // 对字段的别名性注解  
  20.    @XStreamAlias("type")  
  21.    private int messageType;  
  22.    
  23.    // 隐式集合性注解  
  24.    @XStreamImplicit(itemFieldName = "part")  
  25.    private List<String> content;  
  26.    
  27.     
  28.    //将important节点变成属性  
  29.    @XStreamAsAttribute  
  30.    // 将true/false改为yes/no  
  31.    @XStreamConverter(value = BooleanConverter.class, booleans = { false }, strings = {  
  32.         "yes", "no" })  
  33.    private boolean important;  
  34.    
  35.    @XStreamConverter(SingleValueCalendarConverter.class)  
  36.    private Calendar created = new GregorianCalendar();  
  37.    
  38.    public RendezvousMessage(int messageType, boolean important,  
  39.         String... content) {  
  40.       this.messageType = messageType;  
  41.       this.important = important;  
  42.       this.content = Arrays.asList(content);  
  43.    }  
  44. }  
  45.    

结果是:

Java代码  
  1. <message type="15" important="no">  
  2.   <part>first</part>  
  3.   <part>second</part>  
  4.   <created>1387540760390</created>  
  5. </message>  

我们有成功了!!!!

5,使用注解将字段转换为父节点文本内容

我们如果想得到的XML是如下形式:

Java代码  
  1. <message type="15" important="no" created="1154097812245">This is the message content.</message>  

就是将type、important、created三个节点全部变属性,并且将content节点的内容变为父节点message的内容,如何做?

这就需要用到

ToAttributedValueConverter转换器注解

代码如下:

Java代码  
  1. // 新加的转换器注解  
  2. @XStreamConverter(value = ToAttributedValueConverter.class, strings = { "content" })  
  3. // 对类的别名性注解  
  4. @XStreamAlias("message")  
  5. class RendezvousMessage {  
  6.    
  7.    // 将type节点变成属性  
  8.    @XStreamAsAttribute  
  9.    // 对字段的别名性注解  
  10.    @XStreamAlias("type")  
  11.    private int messageType;  
  12.    
  13.    // 隐式集合性注解  
  14.    @XStreamImplicit(itemFieldName = "part")  
  15.    private List<String> content;  
  16.    
  17.    // 将important节点变成属性  
  18.    @XStreamAsAttribute  
  19.    // 将true/false改为yes/no  
  20.    @XStreamConverter(value = BooleanConverter.class, booleans = { false }, strings = {  
  21.         "yes", "no" })  
  22.    private boolean important;  
  23.    
  24.    @XStreamConverter(SingleValueCalendarConverter.class)  
  25.    private Calendar created = new GregorianCalendar();  
  26.    
  27.    public RendezvousMessage(int messageType, boolean important,  
  28.         String... content) {  
  29.       this.messageType = messageType;  
  30.       this.important = important;  
  31.       this.content = Arrays.asList(content);  
  32.    }  
  33. }  

但是运行之后,会发现,运行结果根本与我们预期的不一样,为什么?

因为ToAttributedValueConverter转换器接受的content节点必须是String类型或者有一个转换器将content装换为String类型!!!

例如,将content节点变为String类型:

Java代码  
  1. //新加的转换注解  
  2. @XStreamConverter(value = ToAttributedValueConverter.class, strings = { "content" })  
  3. //对类的别名性注解  
  4. @XStreamAlias("message")  
  5. class RendezvousMessage {  
  6.    
  7.    // 对字段的别名性注解  
  8.    @XStreamAlias("type")  
  9.    private int messageType;  
  10.    
  11.    //由原来的List<String>类型变为String类型  
  12.    private String content;  
  13.     
  14.    
  15.    // 将true/false改为yes/no  
  16.    @XStreamConverter(value = BooleanConverter.class, booleans = { false }, strings = {  
  17.         "yes", "no" })  
  18.    private boolean important;  
  19.    
  20. // @XStreamConverter(SingleValueCalendarConverter.class)  
  21. // private Calendar created = new GregorianCalendar();  
  22.    
  23.    // 再次对构造方法进行了改造  
  24.    public RendezvousMessage(int messageType, boolean important,  
  25.         String content) {  
  26.       this.messageType = messageType;  
  27.       this.important = important;  
  28.       this.content = content;  
  29.    }  
  30. }  

运行结果为:

Java代码  
  1. <message type="15" important="no">这是一大串content节点的内容</message>  

虽然type和important节点没有使用@XStreamAsAttribute注解,但是却被隐式的转换为属性。

6,使用注解忽略某些字段

忽略messageType字段可以使用@XStreamOmitField注解

代码如下:

Java代码  
  1. //对类的别名性注解  
  2. @XStreamAlias("message")  
  3. class RendezvousMessage {  
  4.    
  5.    //忽略messageType字段  
  6.    @XStreamOmitField  
  7.    // 将type节点变成属性  
  8.    @XStreamAsAttribute  
  9.    // 对字段的别名性注解  
  10.    @XStreamAlias("type")  
  11.    private int messageType;  
  12.    
  13.    // 隐式集合性注解  
  14.    @XStreamImplicit(itemFieldName = "part")  
  15.    private List<String> content;  
  16.     
  17.    
  18.    // 将important节点变成属性  
  19.    @XStreamAsAttribute  
  20.    // 将true/false改为yes/no  
  21.    @XStreamConverter(value = BooleanConverter.class, booleans = { false }, strings = {  
  22.         "yes", "no" })  
  23.    private boolean important;  
  24.    
  25.    @XStreamConverter(SingleValueCalendarConverter.class)  
  26.    private Calendar created = new GregorianCalendar();  
  27.    
  28.    // 再次对构造方法进行了改造  
  29.    public RendezvousMessage(int messageType, boolean important,  
  30.         String... content) {  
  31.       this.messageType = messageType;  
  32.       this.important = important;  
  33.       this.content = Arrays.asList(content);  
  34.    }  
  35. }  

运行结果:

Java代码  
  1. <message important="no">  
  2.   <part>first</part>  
  3.   <part>second</part>  
  4.   <created>1387544212500</created>  
  5. </message>  

7,自动检测注解

之前我们启用某个类的注解时,都需要使用processAnnotations方法通知xstream对象解析注解类,其实我们还有一个更简便的模式,即调用autodetectAnnotations(true)方法,让xstream对象自动检测注解类:

Java代码  
  1. public class XStreamTest3 {  
  2.    public static void main(String[] args) {  
  3.       XStream stream = new XStream();  
  4. //    // 通知XStream对象读取并识别RendezvousMessage中的注解  
  5. //    stream.processAnnotations(RendezvousMessage.class);  
  6.       //自动检测注解  
  7.       stream.autodetectAnnotations(true);  
  8.       RendezvousMessage msg = new RendezvousMessage(15, false, "first","second");  
  9.       System.out.println(stream.toXML(msg));  
  10.    }  
  11. }  

注意:1,自动检测注解模式,会使XStream的解析变慢!2,在任何地方调用processAnnotations方法之后,自动检测注解模式将会被关闭。

原文地址:https://www.cnblogs.com/eer123/p/7894908.html