六、使用注解(Annotation)

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

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

有这样一段代码:

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

运行结果是:

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

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

  1. <message>
  2. <type>15</type>
  3. </message>

该怎么办?

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

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

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

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

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

更改main方法如下:

  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的构造方法,新的代码如下:

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

运输出结果如下:

  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格式如下:

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

该怎么办?

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

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

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

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

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

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

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

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

3,注解转换器(XStreamConverter)

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

  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. @XStreamAlias("message")
  12. class RendezvousMessage {
  13. // 对字段的别名性注解
  14. @XStreamAlias("type")
  15. private int messageType;
  16. // 隐式集合性注解
  17. @XStreamImplicit(itemFieldName = "part")
  18. private List<String> content;
  19. private boolean important;
  20. private Calendar created = new GregorianCalendar();
  21. // 再次对构造方法进行了改造
  22. public RendezvousMessage(int messageType, boolean important,
  23. String... content) {
  24. this.messageType = messageType;
  25. this.important = important;
  26. this.content = Arrays.asList(content);
  27. }
  28. }

运行结果如下:

  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>

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

  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,代码如下:

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

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

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

运行结果如下:

  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的类定义:

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

运行结果如下:

  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格式改造成为:

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

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

答案是,使用属性注解:

@XStreamAsAttribute

代码如下:

  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. @XStreamAlias("message")
  13. class RendezvousMessage {
  14. //将type节点变成属性
  15. @XStreamAsAttribute
  16. // 对字段的别名性注解
  17. @XStreamAlias("type")
  18. private int messageType;
  19. // 隐式集合性注解
  20. @XStreamImplicit(itemFieldName = "part")
  21. private List<String> content;
  22. //将important节点变成属性
  23. @XStreamAsAttribute
  24. // 将true/false改为yes/no
  25. @XStreamConverter(value = BooleanConverter.class, booleans = { false }, strings = {
  26. "yes", "no" })
  27. private boolean important;
  28. @XStreamConverter(SingleValueCalendarConverter.class)
  29. private Calendar created = new GregorianCalendar();
  30. public RendezvousMessage(int messageType, boolean important,
  31. String... content) {
  32. this.messageType = messageType;
  33. this.important = important;
  34. this.content = Arrays.asList(content);
  35. }
  36. }

结果是:

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

我们有成功了!!!!

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

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

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

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

这就需要用到

ToAttributedValueConverter转换器注解

代码如下:

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

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

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

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

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

运行结果为:

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

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

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

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

代码如下:

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

运行结果:

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

7,自动检测注解

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

  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方法之后,自动检测注解模式将会被关闭。

使用XStream是实现XML与Java对象的转换(3)--注解的更多相关文章

  1. 使用XStream是实现XML与Java对象的转换(6)--持久化

    九.持久化 在第八节的示例中,当我们操作一组对象时,我们可以指定Writer.OutputStream来写出序列化后的XML数据,我们还可以指定Reader.InputStream来读取序列化后的XM ...

  2. 使用XStream是实现XML与Java对象的转换(4)--转换器

    七.转换器(Converter) 我们程序中的POJO是千变万化的,而且需求也是千奇百怪的,所以XStream中的内置的转换器的功能不一定能够满足我们的要求,所以我们就需要自己构建转换器. 1,一个基 ...

  3. 使用XStream是实现XML与Java对象的转换(1)--简介及入门示例

    一.简单介绍 XStream是thoughtworks开发的开源框架,用于实现XML数据于Java对象.Json数据的转换.它不需要schema或其他的mapping文件就可以进行java对象和xml ...

  4. 使用XStream是实现XML与Java对象的转换(5)--Object Stream

    八,Object Stream 之前的例子我们都是直接输出Xml成为String类型或者从String中获得并解析Xml,现在我们要处理输入流和输出流! 1,输出流(ObjectOutputStrea ...

  5. 使用XStream是实现XML与Java对象的转换(2)--别名

    五.使用别名(Alias) 首先,有这样一段Java代码: import java.util.ArrayList; import java.util.List; import com.thoughtw ...

  6. 不规矩的xml与JAVA对象互相转换的小技巧-使用Marshaller

    摘要:将XML文档与JAVA对象互转是很常见的需求,如果XML定义很规整这很好实现.然而在现实中“不规矩”的XML可能更常见,Marshaller便无能为力了吗?下面是一个小技巧,调整一下思维便能重用 ...

  7. XStream轻松转换xml和java对象

    首先引入所需的jar: xstream-1.4.9.xpp3_min-1.1.4c.dom4j-1.6.1, 或用maven管理jar包时在pom.xml中添加: <!-- https://mv ...

  8. xml-mapping xml 与 java 对象转换映射框架,像 XStream 一样优雅地读写xml

    xml xml 是 java 实现的 xml 框架. 希望以最优雅的方式进行 xml 和 java 之间的转换处理,一行代码搞定一切. 特点 对象的和 xml 的互相映射 支持注解 @Alias 指定 ...

  9. XML 和 java对象相互转换

    XML 和 java对象相互转换 博客分类: XML 和 JSON   下面使用的是JDK自带的类,没有引用任何第三方jar包. Unmarshaller 类使客户端应用程序能够将 XML 数据转换为 ...

随机推荐

  1. [ExtJS5学习笔记]第十八节 Extjs5的panel的dockeditems属性配置toolbar

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/39156321 官方例子:http://docs.sencha.com/extjs/5. ...

  2. android获取设备唯一标示

    概述 有时需要对用户设备进行标识,所以希望能够得到一个稳定可靠并且唯一的识别码.虽然Android系统中提供了这样设备识别码,但是由于Android系统版本.厂商定制系统中的Bug等限制,稳定性和唯一 ...

  3. 任务定义器——SocketProcessor

    将socket扔进线程池前需要定义好任务,要进行哪些逻辑处理由SocketProcessor定义,根据线程池的约定,作为任务必须扩展Runnable.用如下伪代码表示 protected class ...

  4. 微信公众号Unauthorized API function

    在进行微信公众号第三方开发的时候经常遇到这个问题,有两个原因: 1. 你的公众号没有这个api的功能(比如你是个人订阅号等). 2. 你的公众号有这个功能,但是你公众号没有进行认证. 具体可以查看微信 ...

  5. iOS中 本地通知/本地通知详解 韩俊强的博客

    布局如下:(重点讲本地通知) iOS开发者交流QQ群: 446310206 每日更新关注:http://weibo.com/hanjunqiang  新浪微博 Notification是智能手机应用编 ...

  6. Fedora 19: How to resize/extend (LVM) partition?

    Enlarge the disk using fdisk fdisk -l (to see the partition layout, typically we're dealing with /de ...

  7. React Native移植原生Android

    (一)前言 之前已经写过了有关React Native移植原生Android项目的文章,不过因为RN版本更新的原因吧,跟着以前的文章可能会出现一些问题,对于初学者来讲还是会有很多疑难的困惑的,而且官方 ...

  8. UNIX环境高级编程——计算机体系结构基础知识

    无论是在CPU外部接总线的设备还是在CPU内部接总线的设备都有各自的地址范围,都可以像访问内存一样访问,很多体系结构(比如ARM)采用这种方式操作设备,称为等都会产生异常. 通常操作系统把虚拟地址空间 ...

  9. EBS DBA指南笔记(二)

    第三章 监控和诊断   本章涵盖以下几个主题:监测的方法,数据库的监测,apache的监测,forms的监测,并发管理器的监测,服务器的监测,网络的监测,其它的一些监测和诊断方法. 1.监测的方法:主 ...

  10. Gradle 笔记——Java构建入门

    Gradle是一个通用的构建工具,通过它的构建脚本你可以构建任何你想要实现的东西,不过前提是你需要先写好构建脚本的代码.而大部分的项目,它们的构建流程基本是一样的,我们不必为每一个工程都编写它的构建代 ...