zoukankan      html  css  js  c++  java
  • 使用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方法之后,自动检测注解模式将会被关闭。

  • 相关阅读:
    django 定时任务 django-crontab 的使用
    Django中更新多个对象数据与删除对象的方法
    Django复制记录的方法
    Python中关于日期的计算总结
    django中添加日志功能
    Python 日期时间datetime 加一天,减一天,加减一小时一分钟,加减一年
    根据后端传的时间前端js进行倒计时
    输入pip命令报错:from pip import main ImportError: cannot import name 'main'
    操作uwsgi命令
    关于linux下安装mysqlclient报 Failed building wheel for mysqlclient问题
  • 原文地址:https://www.cnblogs.com/eer123/p/7894908.html
Copyright © 2011-2022 走看看