当前位置:编程学习 > XML/UML >>

使用XStream序列化、反序列化XML数据时遇到的各种问题

现在参与的项目是一个纯Application Server,整个Server都是自己搭建的,使用JMS消息实现客户端和服务器的交互,交互的数据格式采用XML。说来惭愧,开始为了赶进度,所有XML消息都是使用字符串拼接的,而XML的解析则是使用DOM方式查找的。我很早就看这些代码不爽了,可惜一直没有时间去重构,最近项目加了几个人,而且美国那边也开始渐渐的把这个项目开发的控制权交给我们了,所以我开始有一些按自己的方式开发的机会了。因而最近动手开始重构这些字符串拼接的代码。

对XML到Java Bean的解析框架,熟悉一点的只有Digester和XStream,Digester貌似只能从XML文件解析成Java Bean对象,所以只能选择XStream来做了,而且同组的其他项目也有在用XStream。一直听说XStream的使用比较简单,而且我对ThoughtWorks这家公司一直比较有好感,所以还以为引入XStream不会花太多时间,然而使用以后才发现XStream并没有想象的你那么简单。不过这个也有可能是因为我不想改变原来的XML数据格式,而之前的XML数据格式的设计自然不会考虑到如何便利的使用XStream。因而记录在使用过程中遇到的问题,供后来人参考,也为自己以后如果打算开其源码提供参考。废话就到这里了,接下来步入正题。

首先对于简单的引用,XStream使用起来确实比较简单,比如自定义标签的属性、使用属性和使用子标签的定义等:

@XStreamAlias("request")
public class XmlRequest1 {
    private static XStream xstream;
    static {
        xstream = new XStream();
        xstream.autodetectAnnotations(true);
    }
  
    @XStreamAsAttribute
    private String from;
  
    @XStreamAsAttribute
    @XStreamAlias("calculate-method")
    private String calculateMethod;
  
    @XStreamAlias("request-time")
    private Date requestTime;
 
    @XStreamAlias("input-files")
    private List<InputFileInfo> inputFiles;
  
    public static String toXml(XmlRequest1 request) {
        StringWriter writer = new StringWriter();
        writer.append(Constants.XML_HEADER);
        xstream.toXML(request, writer);
        return writer.toString();
    }
    public static XmlRequest1 toInstance(String xmlContent) {
        return (XmlRequest1)xstream.fromXML(xmlContent);
}

    @XStreamAlias("input-file")
    public static class InputFileInfo {
        private String type;
        private String fileName;
       
    }
    public static void main(String[] args) {
        XmlRequest1 request = buildXmlRequest();
        System.out.println(XmlRequest1.toXml(request));
    }
    private static XmlRequest1 buildXmlRequest() {
       
    }
}
 对以上Request定义,我们可以得到如下结果:

<?xml version="1.0" encoding="UTF-8"?>
<request from="levin@host" calculate-method="advanced">
 <request-time>2012-11-28 17:11:54.664 UTC</request-time>
 <input-files>
    <input-file>
      <type>DATA</type>
      <fileName>data.2012.11.29.dat</fileName>
    </input-file>
    <input-file>
      <type>CALENDAR</type>
      <fileName>calendar.2012.11.29.dat</fileName>
    </input-file>
 </input-files>
</request>
可惜这个世界不会那么清净,这个格式有些时候貌似并不符合要求,比如request-time的格式、input-files的格式,我们实际需要的格式是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<request from="levin@host" calculate-method="advanced">
 <request-time>20121128T17:51:05</request-time>
 <input-file type="DATA">data.2012.11.29.dat</input-file>
 <input-file type="CALENDAR">calendar.2012.11.29.dat</input-file>
</request>
对不同Date格式的支持可以是用Converter实现,在XStream中默认使用自己实现的DateConverter,它支持的格式是:yyyy-MM-dd HH:mm:ss.S 'UTC',然而我们现在需要的格式是yyyy-MM-dd’T’HH:mm:ss,如果使用XStream直接注册DateConverter,可以使用配置自己的DateConverter,但是由于DateConverter的构造函数的定义以及@XStreamConverter的构造函数参数的支持方式的限制,貌似DateConverter不能很好的支持注解方式的注册,因而我时间了一个自己的DateConverter以支持注解:

public class LevinDateConverter extends DateConverter {
    public LevinDateConverter(String dateFormat) {
        super(dateFormat, new String[] { dateFormat });
    }
}
在requestTime字段中需要加入以下注解定义:

@XStreamConverter(value=LevinDateConverter.class, strings={"yyyyMMdd'T'HH:mm:ss"})
@XStreamAlias("request-time")
private Date requestTime;
对集合类,XStream提供了@XStreamImplicit注解,以将集合中的内容摊平到上一层XML元素中,其中itemFieldName的值为其使用的标签名,此时InputFileInfo类中不需要@XStreamAlias标签的定义:

@XStreamImplicit(itemFieldName="input-file")
private List<InputFileInfo> inputFiles;
对InputFileInfo中的字段,type作为属性很容易,只要为它加上@XStreamAsAttribute注解即可,而将fileName作为input-file标签的一个内容字符串,则需要使用ToAttributedValueConverter,其中Converter的参数为需要作为字符串内容的字段名:

@XStreamConverter(value=ToAttributedValueConverter.class, strings={"fileName"})
public static class InputFileInfo {
    @XStreamAsAttribute
    private String type;
private String fileName;

}
XStream对枚举类型的支持貌似不怎么好,默认注册的EnumSingleValueConverter只是使用了Enum提供的name()和静态的valueOf()方法将enum转换成String或将String转换回enum。然而有些时候XML的字符串和类定义的enum值并不完全匹配,最常见的就是大小写的不匹配,此时需要写自己的Converter。在这种情况下,我一般会在enum中定义一个name属性,这样就可以自定义enum的字符串表示。比如有TimePeriod的enum:

public enum TimePeriod {
    MONTHLY("monthly"), WEEKLY("weekly"), DAILY("daily");
  
    private String name;
  
    public String getName() {
        return name;
    }
  
    private TimePeriod(String name) {
        this.name = name;
    }
  
    public static TimePeriod toEnum(String timePeriod) {
        try {
            return Enum.valueOf(TimePeriod.class, timePeriod);
        } catch(Exception ex) {
     &n

补充:综合编程 , 其他综合 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,