xstream简介(bean与xml转换工具)
本文
解决了"_"显示为"__"的问题。
解决了当xml的element属性多余bean的映射问题
解决了生成xml换行的问题
作用:
能够简单的通过
xstreamInstance.fromXML(xmlContent); 获取到对象
xstreamInstance.toXML(beanInstance); 将对象转换为xml
官网:
http://xstream.codehaus.org/
适用场景
bean与xml存在相互转换的场景:配置文件读取,报文解析,生成报文
从官方的版本演进上看,还提供了hibernate,json配置文件的读取。从1.4版本以后,新加入hibernate相关处理的逻辑,该部分功能尚未详细了解。
不适用场景
xml文件较大(比如有个10M的xml文件解析,就不太适用,因一次性解析,以及生成对象将会造成内存溢出)
效率
经过实测对象10个属性,对应xml。
xstream采用默认xml引擎,效率是dom4j解析的10倍左右(具体效率与代码实现有关,dom4j也能实现较高性能的解析)
总体来说,利用xstream能够大大提高,xml解析的入门,以及编写方便
具体事例:
如何应用:
从xstream的pom文件分析
只需要将xstream.jar xpp3.jar xmlpull.jar放入项目即可
如果为maven项目
只需要添加:
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.3</version>
</dependency>
使用实例:
例子一:
定义类
public class Person {
private String firstname;
private String lastname;
private PhoneNumber phone;
private PhoneNumber fax;
// ... constructors and methods
}
public class PhoneNumber {
private int code;
private String number;
// ... constructors and methods
}
初始化解析对象
XStream xstream = new XStream();
如果不想使用xpp作为xml解析引擎,也可写为new XStream(new Dom4jDriver());
设置映射规则:
xstream.alias("person", Person.class);
xstream.alias("phonenumber", PhoneNumber.class);
将对象序列化为xml
Person joe = new Person("Joe", "Walnes");
joe.setPhone(new PhoneNumber(123, "1234-456"));
joe.setFax(new PhoneNumber(123, "9999-999"));
String xml = xstream.toXML(joe);
结果:
<person>
<firstname>Joe</firstname>
<lastname>Walnes</lastname>
<phone>
<code>123</code>
<number>1234-456</number>
</phone>
<fax>
<code>123</code>
<number>9999-999</number>
</fax>
</person>
由xml生成对象
Person newJoe = (Person)xstream.fromXML(xml);
当然xstream也支持注解,一下代码中使用到了 XstreamUtils 这个是在xstream基础上的封装,解决了现在xstream在日常使用中会出现的一些问题
@XStreamAlias("TestBeanRoot")
class TestBean
{
private String testEl1;
@XStreamAlias("test_Rename_El2")
private String testEl2;
@XStreamAsAttribute
private String testAttr;
@XStreamOmitField
private String testSkip;
@XStreamImplicit(itemFieldName = "itemName", keyFieldName = "keyName")
private List<String> testList;
...getAndSet
}
XStream testBeanXstream = XstreamUtils.getXstream(XstreamUtilsTest.TestBean.class);
XstreamUtilsTest.TestBean tt = createTestBean();
System.out.println(testBeanXstream.toXML(tt));
"<TestBeanRoot testAttr=\"attr\">"
"<testEl1>el1</testEl1>"
+ "<test_Rename_El2>el2</test_Rename_El2>"
+ "<itemName>listItem1</itemName>"
+ "<itemName>listItem2</itemName>"
+ "</TestBeanRoot>"
如果存在多级el的情况,可以通过引用其他类的形式 如 private TestBean2 testBean2;,新的类内部又可以具有其他结构
xstream常用的注解有:
@XStreamAlias
@XStreamAsAttribute
@XStreamOmitField
@XStreamImplicit
但看到很多文章,有这样的说法
1、xstream有bug,在转换过程中,会将 定义别名中的下划线“_”转换为xml后会变成“__”
2、在xml生成对象时,xml有多出来的元素时,对象生成将会抛出异常。
事实上从xstream源代码分析,这两个都不是问题,xstream都提供了对应的处理方式
具体参看工具类
/**
* xstream工具封装
* 用以处理xml与bean的转换
*
* @author PengQingyang
* @version [版本号, 2012-10-5]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class XstreamUtils
{
private static Logger logger = LoggerFactory.getLogger(XstreamUtils.class);
private static Map<Class<?>, XStream> xstreamMap = new WeakHashMap<Class<?>, XStream>();
/**
* 转换过程中特殊字符转码
*/
private static NameCoder nameCoder = new NameCoder()
{
public String encodeNode(String arg0)
{
return arg0;
}
public String encodeAttribute(String arg0)
{
return arg0;
}
public String decodeNode(String arg0)
{
return arg0;
}
public String decodeAttribute(String arg0)
{
return arg0;
}
};
/**
* 在xml中多余的节点生成bean时会抛出异常
* 通过该mapperWrapper跳过不存在的属性
* @param mapper
* @return [参数说明]
*
* @return MapperWrapper [返回类型说明]
* @exception throws [异常类型] [异常说明]
* @see [类、类#方法、类#成员]
*/
private static MapperWrapper createSkipOverElementMapperWrapper(
Mapper mapper)
{
MapperWrapper resMapper = new MapperWrapper(mapper)
{
/**
* @param elementName
* @return
*/
@SuppressWarnings("rawtypes")
@Override
public Class realClass(String elementName)
{
Class res = null;
;
try
{
res = super.realClass(elementName);
}
catch (CannotResolveClassException e)
{
logger.warn("xstream change xml to object. filed (0) not exsit. ",
elementName);
}
return res;
}
};
return resMapper;
}
/**
* 获取xstream转换对象
* @param classType
* @return [参数说明]
*
* @return XStream [返回类型说明]
* @exception throws [异常类型] [异常说明]
* @see [类、类#方法、类#成员]
*/
public static XStream getXstream(Class<?> classType)
{
return getXstream(classType, true);
}
/**
* 获取xstream转换对象
* @param classType
* @param isSkipOverElement
* @return [参数说明]
*
* @return XStream [返回类型说明]
* @exception throws [异常类型] [异常说明]
* @see [类、类#方法、类#成员]
*/
public static XStream getXstream(Class<?> classType,
boolean isSkipOverElement)
{
if (xstreamMap.containsKey(classType))
{
return xstreamMap.get(classType);
}
XStream res = null;
if (isSkipOverElement)
{
res = new XStream(new Xpp3DomDriver(nameCoder))
{
/**
* @param next
* @return
*/
protected MapperWrapper wrapMapper(MapperWrapper next)
{
return createSkipOverElementMapperWrapper(next);
}
};
}
else
{
res = new XStream(new Xpp3DomDriver(nameCoder));
}
logger.info("create xstream by {0} , parameter {1}", new Object[] {
classType.getName(), isSkipOverElement });
res.processAnnotations(classType);
xstreamMap.put(classType, res);
return res;
}
}
封装后的使用:
private static xxxxXstream = XstreamUtils.getXstream(Xxxx.class);
method(){
xxxxXstream.toXML
xxxxXstream.fromXML
}
2012-10-22追加
如果想生成的xml是否换行,自己进行控制,可这样写
/**
*<获取xstream转换对象>
*<功能详细描述>
* @param classType
* @param isSkipOverElement
* @param isNewLine
* @return [参数说明]
*
* @return XStream [返回类型说明]
* @exception throws [异常类型] [异常说明]
* @see [类、类#方法、类#成员]
*/
public static XStream getXstream(Class<?> classType,
boolean isSkipOverElement, boolean isNewLine) {
if (xstreamMap.containsKey(classType)) {
return xstreamMap.get(classType);
}
/**
* 生成domDriver 重写createWriter方法,使生成的domDriver在新的节点不会信生成一行
*/
HierarchicalStreamDriver domDriver = null;
if (isNewLine) {
domDriver = new Xpp3DomDriver(nameCoder);
} else {
domDriver = new Xpp3DomDriver(nameCoder) {
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out, getNameCoder()) {
protected String getNewLine() {
return "";
}
};
}
};
}
XStream res = null;
if (isSkipOverElement) {
res = new XStream(domDriver) {
protected MapperWrapper wrapMapper(MapperWrapper next) {
return createSkipOverElementMapperWrapper(next);
}
};
} else {
res = new XStream(domDriver);
}
logger.info("create xstream by {0} , parameter {1}", new Object[] {
classType.getName(), isSkipOverElement });
res.processAnnotations(classType);
xstreamMap.put(classType, res);
return res;
}