0. 介绍
Xstream是一种OXMapping 技术,是用来处理XML文件序列化的框架,在将javaBean序列化,或将XML文件反序列化的时候,不需要其它辅助类和映射文件,使得XML序列化不再繁琐。(来自百度百科)
1. 准备工作
如果大家用的是Maven,直接引用即可:
com.thoughtworks.xstream
xstream
1.4.7
如果不使用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);
打印结果:
tommy
123456
每个下划线都额外多了一个下划线。
解决办法是,继承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 xstreamWrapper = new HashMap();
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 xstreamWrapper = new HashMap();
private static final String CHINA_TIME_ZONE = "Asia/Shanghai";
private static final String XML_BEGIN_STR = "";
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("_-", "_");
}
}