Xstream使用小结

0.      介绍

Xstream是一种OXMapping 技术,是用来处理XML文件序列化的框架,在将javaBean序列化,或将XML文件反序列化的时候,不需要其它辅助类和映射文件,使得XML序列化不再繁琐。(来自百度百科)


1.      准备工作

如果大家用的是Maven,直接引用即可:

<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.7</version>
</dependency>

如果不使用maven直接下载jar包的话,需要xmlpull-1.1.3.1.jar,xstream-1.4.7.jar这两个jar。


2.      简单应用

XML与java对象互相转换的Demo如下:

package com.utils;

import java.io.File;
import java.util.TimeZone;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.basic.DateConverter;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;

/**
 * xml处理组件
 * 
 * @author
 */
public class XmlUtil1 {

    public static final XmlFriendlyNameCoder nameCoder = new XmlFriendlyNameCoder();

    // 编码格式
    private static final String ENCODING = "UTF-8";

    // dom解析驱动
    private static final DomDriver fixDriver = new DomDriver(ENCODING, nameCoder);

    // 通用解析器
    public static final XStream XSTREAM = new XStream(fixDriver);

    private static final String CHINA_TIME_ZONE = "Asia/Shanghai";

    static {
        // 时区处理
        TimeZone zone = TimeZone.getTimeZone(CHINA_TIME_ZONE);
        XSTREAM.registerConverter(new DateConverter(zone), XStream.PRIORITY_NORMAL);
        XSTREAM.autodetectAnnotations(true);

    }

    private XmlUtil1() {
    }

    public static void main(String[] args) {
        // 让Root节点的XML与AReq对象关联
        XSTREAM.alias("Root", AReq.class);

        // 对象生成XML
        AReq req1 = new AReq();
        req1.setName("Tommy");
        req1.setPasswd("123456");
        String areqStr = XSTREAM.toXML(req1);
        System.out.println("Areq:\n" + areqStr);
        System.out.println("-------------------------");

        // XML字符串生成对象
        AReq req2 = (AReq) XSTREAM.fromXML(areqStr);
        System.out.println("req2=" + req2);
        System.out.println("req1==req2 is " + (req1 == req2));
        System.out.println("-------------------------");

        // XML文件生成对象
        AReq req3 = (AReq) XSTREAM.fromXML(new File("d:/test/aReq.xml"));
        System.out.println("req3=" + req3);
        System.out.println("-------------------------");

        // XStream还支持输入流、URL等方式生成对象。
    }

}

其中AReq.java如下:

package com.utils;

import com.thoughtworks.xstream.annotations.XStreamAlias;

@XStreamAlias("Root")
public class AReq {
    /**
     * 账号
     */
    @XStreamAlias("name")
    private String name;
    /**
     * 密码
     */
    @XStreamAlias("passwd")
    private String passwd;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPasswd() {
        return passwd;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

}

从这个demo中可以看出Xstream的便捷之处。


3.      使用中碰到的问题及解决方案

第一个问题是,若xml节点中包含下划线,对象转换XML时,会产生2个下划线的问题。如下请求对象类:

package com.utils;

import com.thoughtworks.xstream.annotations.XStreamAlias;

@XStreamAlias("Root")
public class BReq {
    /**
     * 账号
     */
    @XStreamAlias("first_name")
    private String name;
    /**
     * 密码
     */
    @XStreamAlias("first_passwd")
    private String passwd;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPasswd() {
        return passwd;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

}

此时转换对象为XML:

        XSTREAM.alias("Root", BReq.class);
        BReq breq1 = new BReq();
        breq1.setName("tommy");
        breq1.setPasswd("123456");
        String breqStr = XSTREAM.toXML(breq1);
        System.out.println(breqStr);

打印结果:

<Root>
  <first__name>tommy</first__name>
  <first__passwd>123456</first__passwd>
</Root>

每个下划线都额外多了一个下划线。

解决办法是,继承XmlFriendlyNameCoder类,如下:

/**
 * 重写NameCoder,解决单下划线变双下划线的问题
 * 
 * @author maoxiaoyong
 * 
 */
class XStreamNameCoder extends XmlFriendlyNameCoder {
    public XStreamNameCoder() {
        super("_-", "_");
    }
}

然后nameCoder使用此实现类构造。


第二个问题是,根节点为Root的XML请求会有很多,如何解决冲突的问题。

如果每次使用的时候,都调用一次XSTREAM.alias("Root", AReq.class);会有并发问题。如果每次都new一个新的XStream对象,又会有效率问题。我的解决方案是,为每一个请求分配一个专属的XStream解析器,在初始化块中初始化。如下:

// 每个接口对接一个专用解析器
private static final Map<String, XStream> xstreamWrapper = new HashMap<String, XStream>();
static {
        // 时区处理
        TimeZone zone = TimeZone.getTimeZone(CHINA_TIME_ZONE);
        XSTREAM.registerConverter(new DateConverter(zone), XStream.PRIORITY_NORMAL);
        XSTREAM.autodetectAnnotations(true);

                XStream xstream;
        // AReq申请专用
        xstream = new XStream(fixDriver);
        xstream.autodetectAnnotations(true);
        xstream.alias("Root", AReq.class);
        xstreamWrapper.put("UseAreq", xstream);
        
        // BReq申请专用
        // ...

    }

使用的时候,可以多传递一个参数:

    /**
     * 对象转报文
     * 
     * @param wrapperKey 解析器获取。要传入合法的键值。为空时取默认值
     * @param obj 待转换的对象
     * @return
     */
    public static String toXML(String wrapperKey, Object obj) {
        XStream xstream = XSTREAM;
        if (StringUtils.isNotEmpty(wrapperKey)) {
            xstream = xstreamWrapper.get(wrapperKey);
        }
        return xstream.toXML(obj);
    }

    /**
     * 报文转对象
     * 
     * @param wrapperKey 解析器获取。要传入合法的键值。为空时取默认值
     * @param xml 待解析的xml串
     * @return Object
     */
    public static Object fromXML(String wrapperKey, String xml) {
        Object target = null;
        XStream xstream = XSTREAM;
        if (StringUtils.isNotEmpty(wrapperKey)) {
            xstream = xstreamWrapper.get(wrapperKey);
        }
        try {
            target = xstream.fromXML(xml);
        } catch (RuntimeException e) {
            LOGGER.error("解析XML错误,解析器名称【" + wrapperKey + "】报文结构:" + xml, e);
        }
        return target;
    }

目前碰到的问题就这两个,下面给出完整的XStream工具类,大家可以根据自己的需求,进行改造。

package com.utils;

import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.basic.DateConverter;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;

/**
 * xml处理组件
 * 
 * @author
 */
public class XmlUtil {

    private static final Logger LOGGER = Logger.getLogger(XmlUtil.class);

    public static final XStreamNameCoder nameCoder = new XStreamNameCoder();

    // 编码格式
    private static final String ENCODING = "UTF-8";

    // dom解析驱动
    private static final DomDriver fixDriver = new DomDriver(ENCODING, nameCoder);

    // 通用解析器
    public static final XStream XSTREAM = new XStream(fixDriver);

    // 每个接口对接一个专用解析器
    private static final Map<String, XStream> xstreamWrapper = new HashMap<String, XStream>();

    private static final String CHINA_TIME_ZONE = "Asia/Shanghai";

    private static final String XML_BEGIN_STR = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";

    static {
        // 时区处理
        TimeZone zone = TimeZone.getTimeZone(CHINA_TIME_ZONE);
        XSTREAM.registerConverter(new DateConverter(zone), XStream.PRIORITY_NORMAL);
        XSTREAM.autodetectAnnotations(true);

        XStream xstream;
        // AReq申请专用
        xstream = new XStream(fixDriver);
        xstream.autodetectAnnotations(true);
        xstream.alias("Root", AReq.class);
        xstreamWrapper.put("UseAreq", xstream);

        // BReq申请专用
        xstream = new XStream(fixDriver);
        xstream.autodetectAnnotations(true);
        xstream.alias("Root", BReq.class);
        xstreamWrapper.put("UseBreq", xstream);

    }

    private XmlUtil() {
    }

    /**
     * 报文转对象
     * 
     * @param wrapperKey 解析器获取。要传入合法的键值。为空时取默认值
     * @param xml 待解析的xml串
     * @return Object
     */
    public static Object fromXML(String wrapperKey, String xml) {
        Object target = null;
        XStream xstream = XSTREAM;
        if (StringUtils.isNotEmpty(wrapperKey)) {
            xstream = xstreamWrapper.get(wrapperKey);
        }
        try {
            target = xstream.fromXML(xml);
        } catch (RuntimeException e) {
            LOGGER.error("解析XML错误,解析器名称【" + wrapperKey + "】报文结构:" + xml, e);
        }
        return target;
    }

    /**
     * 对象转报文
     * 
     * @param wrapperKey 解析器获取。要传入合法的键值。为空时取默认值
     * @param obj 待转换的对象
     * @return
     */
    public static String toXML(String wrapperKey, Object obj) {
        XStream xstream = XSTREAM;
        if (StringUtils.isNotEmpty(wrapperKey)) {
            xstream = xstreamWrapper.get(wrapperKey);
        }
        return xstream.toXML(obj);
    }

    /**
     * 去掉xml多余的空白、换行符
     */
    public static String trimXml(String xml) {
        String result = xml;
        if (null != result) {
            result = result.replaceAll(">\\s+<", "><");
        }
        return result;
    }

    /**
     * xml增加开头
     * 
     * @param xml
     * @return
     */
    public static String addXmlBegin(String xml) {
        return XML_BEGIN_STR + xml;
    }

}

/**
 * 重写NameCoder,解决单下划线变双下划线的问题
 * 
 * @author maoxiaoyong
 * 
 */
class XStreamNameCoder extends XmlFriendlyNameCoder {
    public XStreamNameCoder() {
        super("_-", "_");
    }
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值