XStream反序列化的坑

先附上XStream官方文档和API地址:

官方文档http://x-stream.github.io/annotations-tutorial.html

API文档http://x-stream.github.io/javadoc/


我在项目中使用了XStream来完成XML报文与Java Bean的转换操作,Bean序列化没有什么坑,但在反序列化时,遇到了一个较大的坑,踩了挺久。
 因为驼峰命名规范的字段名与接口中规定报文字段名有所出入,接口中规定的格式有下划线,所以需要进行别名处理,但序列化时不好使!
 最后从结果看来是有一个坑,加上自己有些理所当然。。

现在总结一下:
 下面只谈及反序列化过程。。。。也就是XML转Java Bean
 为了简化开发,在使用XStream时,使用了注解,所以为了注解生效需要开启注解,这里注意反序列化必须通过调用processAnnotations()来开启注解,不能通过autodetectAnnotations()开启 !!!

先放上XML和Bean

需要被解析的XML报文

<?xml version="1.0" encoding="utf-8"?>

<business_trans version="1.0">
  <response_type>barcode_pay_response</response_type>
  <isspid>000000</isspid>
  <pos_id>0</pos_id>
  <pos_seq>000000</pos_seq>
  <result>
    <id>7000</id>
    <comment>没有该终端</comment>
  </result>
</business_trans>

需要XML反序列化成的Bean

 

 原因分析:
通过autodetectAnnotations()这种懒加载方式开启, XStream实例缓存为注解处理的所有类类型。每次XStream转换一个对象时,它将在自动检测模式下首先处理对象的类型和所有相关的类型。因此,将对象序列化为XML是没有问题的,因为XStream已经加载了该类,扫描了注解,预先知道了其中的所有类型。
在反序列化时不再是这样。XStream必须知道别名才能将其转换为适当的类型,但是只有在预先处理了该类型之后,它才能找到别名的注释。因此,如果类型还没有反序列化,则反序列化将失败。

参考代码:

    static {
        //指定解析编码为GBK,并解决XStream对出现双下划线的bug
        xStreamForReqData = new XStream(new DomDriver("GBK", new XmlFriendlyNameCoder("-_", 
        "_")));
        //开启注解(在类加载时调用一次,在后续每次调用序列化方法时会开启当前类的注解)
        xStreamForReqData.autodetectAnnotations(true);
    }

    public static String objToXML(Object xmlData) throws Exception{
        String postDataXML;
        try {
            //将要提交给API的数据对象转换成XML格式数据Post给API
            String postDataXMLOld = xStreamForReqData.toXML(xmlData);
            postDataXML = postDataXMLOld.replace("&quot;", "\"");
            //为Bean转换后的XML加上头信息
            postDataXML = XML_TAG + postDataXML;
        } catch (Exception e) {
            logger.error("Object converts into XML go wrong", e);
            throw e;
        }
        return postDataXML;
    }

从上面这段代码分析,即从序列化角度来看,先调用方法开启,但实质在调用序列化方法序列化该Bean时,扫描该Bean注解,别名注解此时生效,作用相当于将Bean的字段直接重命名,然后做为XML标签名或者属性名。因为在选择序列化该Bean时,就已经能加载到该Bean,所以不会有问题。


参考代码:

     public static Object xmlToObj(String xmlString) throws Exception{
        Object obj;
        try {
            //指定解析编码为GBK
            XStream xStreamForResData = new XStream(new DomDriver("GBK"));
            xStreamForResData.autodetectAnnotations(true);
            obj = xStreamForResData.fromXML(xmlString);
        } catch (Exception e) {
            logger.error("XML converts into Object go wrong", e);
            throw e;
        }
        return obj;
    }

反序列化时所报的错

 但在反序列化时,这种懒加载导致还先未进行别名处理,就直接调用反序列化方法,它直接去根据根标签去加载类名为“business_trans”的类,加载到该类后,因为懒加载,这是才开始扫描XStream相关注解,但显然你的别名注解@XStreamAlias("business_trans")生效在后,加载类名为“business_trans”的类在前,此时你的Bean名称还未别名过,还为“YimaScanResData”,所以肯定找不到该类,所以反序列化肯定报错。

当然,如果在反序列化之前就已经扫描了该类的注解,那么你再反序列化就不会报错,因为在反序列化之前注解已经生效。比如通过这种延迟加载开启注解的方式,你先进行了该Bean的序列化,在进行XML反序列化该Bean,就不存在问题。如下图所示:

 

但还是不建议在反序列化时,使用这种注解开启方式。。


反序列化正确姿势:

    public static Object xmlToObj(String xmlString, Class clazz) throws Exception{
        Object obj;
        try {
            //指定解析编码为GBK
            XStream xStreamForResData = new XStream(new DomDriver("GBK"));
            xStreamForResData.processAnnotations(clazz);
            obj = xStreamForResData.fromXML(xmlString);
        } catch (Exception e) {
            logger.error("XML converts into Object go wrong", e);
            throw e;
        }
        return obj;
    }

对于fromXML(),他有多种重载形式,这里我只用过两种一种如上,fromXML(java.lang.String xml),因为当前XStream实例只扫描了当前这一个class,所以找别名为“business_trans”,直接就能对应到这个类,然后反序列化。另一种如下,是提供一个所要反序列化的对象实例,然后XStream将该XML填充至该实例,同样别名注解预加载生效了,所以字段名是可以一一对应解析正确的!

    public static Object xmlToObj(String xmlString, Object obj) throws Exception{
        try {
            //指定解析编码为GBK
            XStream xStreamForResData = new XStream(new DomDriver("GBK"));
            xStreamForResData.processAnnotations(obj.getClass());
            obj = xStreamForResData.fromXML(xmlString, obj);
        } catch (Exception e) {
            logger.error("XML converts into Object go wrong", e);
            throw e;
        }
        return obj;
    }

反序列化后的实体Bean

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值