基于Xstream的Xml工具类
write by donaldhan, 2017-12-06 16:20- 引言
- 实战演练
- 附
引言
在使用Xstream转化bean和xml之间,处理字符串没有任何问题,但当遇到 Integer,Double,Date 等类型时,往往由于xml中空字符串属性导致转换bean的Integer,Double,Date类型属性异常,同时在bean转化为xml时,Date 类型属性输出到xml中为ISO格式,非 yyyy-MM-dd HH:mm:ss 格式,这是将会用到转换器converters。
实战演练
maven依赖
我们通过一个xml工具类来看,首先引入Xstream maven依赖。
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.8</version>
</dependency>
转换器
我们先来看一个Double类型转换器:
/**
* @author donald
* @date 2017-12-6
* @time 下午2:55:55
*/
public class DoubleExtConverter extends AbstractSingleValueConverter {
private static NumberFormat numberFormat = NumberFormat.getInstance();
static{
numberFormat.setGroupingUsed(false);
numberFormat.setMaximumFractionDigits(6);
numberFormat.setMinimumFractionDigits(6);
}
@SuppressWarnings("unchecked")
@Override
public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
return type.isAssignableFrom(Double.class);
}
@Override
public String toString(Object obj) {
return obj != null ? numberFormat.format(obj) : null;
}
@Override
public Object fromString(String str) {
Double obj = null;
if (StringHelper.isNotBlank(str) && !str.equals("")) {
obj = Double.valueOf(str);
}
return obj;
}
}
我们为什么要继承 AbstractSingleValueConverter 呢?我们来看一下 AbstractSingleValueConverter 定义。
public abstract class AbstractSingleValueConverter implements SingleValueConverter {
public abstract boolean canConvert(Class type);
public String toString(Object obj) {
return obj == null ? null : obj.toString();
}
public abstract Object fromString(String str);
}
再来看 SingleValueConverter 接口的定义。
public interface SingleValueConverter extends ConverterMatcher {
/**
* Marshals an Object into a single value representation.
* @param obj the Object to be converted
* @return a String with the single value of the Object or <code>null</code>
*/
public String toString(Object obj);
/**
* Unmarshals an Object from its single value representation.
* @param str the String with the single value of the Object
* @return the Object
*/
public Object fromString(String str);
}
SingleValueConverter 接口有两个方法一个是 toString ,一个是 fromString ,toString 方法用于将bean属性转化为String写到xml中,fromString用于从xml中读取属性字符串转换为bean的属性。从上面的分析可以导出,想要处理bean和xml转换过程中,非 String 类型,可以实现 AbstractSingleValueConverter 重写 toString 和 fromString 方法,具体转换器可以参考官方网站converters。
辅助工具类
为了自定义转换器我们需要先定义,辅助工具类。
字符串工具类
package util;
/**
* @author donald
* @date 2017-12-6
* @time 下午2:52:01
*/
public class StringHelper {
/**
* @param value
* @return
*/
public static boolean isBlank(String value){
boolean isBlank = false;
if(value == null || value.equals("")){
isBlank = true;
}
return isBlank;
}
/**
* @param value
* @return
*/
public static boolean isNotBlank(String value){
return !isBlank(value);
}
}
时间工具类
package util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author donald
* 2017年12月6日
* 下午4:12:18
*/
public class DateUtil {
private static final Logger log = LoggerFactory.getLogger(DateUtil.class);
private final static SimpleDateFormat sdfDay = new SimpleDateFormat("yyyy-MM-dd");
private final static SimpleDateFormat sdfTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 获取YYYY-MM-DD HH:mm:ss格式
*
* @return
*/
public static String getTime() {
return sdfTime.format(new Date());
}
/**
* 获取YYYY-MM-DD HH:mm:ss格式
* @param date
* @return
*/
public static String getTime(Date date) {
return sdfTime.format(date);
}
/**
* 格式化日期
*
* @return
*/
public static Date formatDate(String date) {
Date result = null;
try {
result = sdfDay.parse(date);
} catch (ParseException e) {
log.error("格式化日期出错",e);
}
return result;
}
/**
* @param date
* @return
*/
public static Date formatTime(String date) {
Date result = null;
try {
result = sdfTime.parse(date);
} catch (ParseException e) {
log.error("格式化时间出错",e);
}
return result;
}
}
转换器实现
我们这篇文章仅实现 Date,Integer,Double 转换器,Xstream 其实,已经有 Date,Integer,Double 等Java原始类型的转换器,但处理null有问题,下面定制 Date,Integer,Double 转换器。
日期转换器
package util.converter;
import java.util.Date;
import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
import util.DateUtil;
import util.StringHelper;
/**
* @author donald
* @date 2017-12-6
* @time 下午2:55:48
*/
public class DateExtConverter extends AbstractSingleValueConverter {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public boolean canConvert(Class type) {
return type.isAssignableFrom(Date.class);
}
@Override
public String toString(Object obj) {
return obj != null ? DateUtil.getTime((Date)obj) : null;
}
@Override
public Object fromString(String str) {
Date date = null;
if (StringHelper.isNotBlank(str)) {
date = DateUtil.formatTime(str);
}
return date;
}
}
Integer转换器
package util.converter;
import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
/**
* @author donald
* @date 2017-12-6
* @time 下午2:55:55
*/
public class IntegerExtConverter extends AbstractSingleValueConverter {
@SuppressWarnings("unchecked")
public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
return type.isAssignableFrom(Integer.class);
}
@Override
public String toString(Object obj) {
return obj != null ? String.valueOf(obj) : null;
}
@Override
public Object fromString(String str) {
Integer obj = null;
if (StringHelper.isNotBlank(str) && !str.equals("")) {
obj = Integer.valueOf(str);
}
return obj;
}
}
Xml工具类
在上一节,我们定制了Date,Integer,Double 转换器,我们将这些转换器注册到XStream中,具体如下:
package util;
import java.util.Map;
import java.util.Map.Entry;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import util.converter.DateExtConverter;
import util.converter.DoubleExtConverter;
import util.converter.IntegerExtConverter;
/**
* 对象与Xml字符串转化工具
* @author donald
* 2017年7月8日
* 下午5:15:19
*/
public class XmlUtil {
private static final XStream xstream;
/** xml文件头*/
public static final String XML_HEADER = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
static {
xstream = new XStream(new DomDriver("utf-8"));
xstream.registerConverter(new DoubleExtConverter());
xstream.registerConverter(new DateExtConverter());
xstream.registerConverter(new IntegerExtConverter());
xstream.autodetectAnnotations(true);
}
/**
* 转化对象为Xml字符串
* @param obj
* @return
*/
public static String toXml(Object obj) {
xstream.processAnnotations(obj.getClass()); //处理XSteam注解
return xstream.toXML(obj);
}
/**
* 转化对象为Xml字符串,并添加字符串
* @param obj
* @return
*/
public static String toXmlWithHeader(Object obj) {
return XML_HEADER + toXml(obj);
}
/**
* 转化XML为指定类
* @param xmlStr
* @param cls
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T toBean(String xmlStr, Class<T> clazz) {
xstream.processAnnotations(clazz);
T obj = (T) xstream.fromXML(xmlStr);
return obj;
}
/**
* 转化XML为指定类,并将转换别与类的映射关系
* @param xmlStr
* @param clazz
* @param aliasMap,别名与类的映射,将别名转换为类
* @return
*/
public static <T> T toBeanWithAlias(String xmlStr, Class<T> clazz, Map<String, Class<?>> aliasMap) {
xstream.processAnnotations(clazz);
if(null != aliasMap && 0 < aliasMap.size()) {
for (Entry<String, Class<?>> entry : aliasMap.entrySet()) {
xstream.alias(entry.getKey(), entry.getValue());
}
}
@SuppressWarnings("unchecked")
T obj = (T) xstream.fromXML(xmlStr);
return obj;
}
}
测试
准备工作已完成,现在我们来测试一下,创建测试实体类:
package util.entity;
import java.io.Serializable;
import java.util.Date;
import com.thoughtworks.xstream.annotations.XStreamAlias;
@XStreamAlias("person")
public class Person implements Serializable{
/**
*
*/
private static final long serialVersionUID = -5680738148261277921L;
private String name;
private Integer age;
private Date birthDay;
private Double stature;
public Person(String name, Integer age, Date birthDay, Double stature) {
super();
this.name = name;
this.age = age;
this.birthDay = birthDay;
this.stature = stature;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthDay() {
return birthDay;
}
public void setBirthDay(Date birthDay) {
this.birthDay = birthDay;
}
public Double getStature() {
return stature;
}
public void setStature(Double stature) {
this.stature = stature;
}
}
创建测试类:
package util;
import static org.junit.Assert.*;
import java.util.Date;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.entity.Person;
/**
* @author donald
* 2017年12月6日
* 下午9:48:29
*/
public class XmlUtilTests {
private static final Logger log = LoggerFactory.getLogger(XmlUtilTests.class);
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void testToXml() {
Person person = new Person("donald",23,new Date(),178.6);
String result = XmlUtil.toXmlWithHeader(person);
log.info("person to xml:\n{}",result);
/*
String expXml =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"+
"<person>"+
"<name>donald</name>"+
"<age>23</age>"+
"<birthDay>2017-12-06 21:50:25</birthDay>"+
"<stature>178.6</stature>"+
"</person>";
Assert.assertEquals("转换失败", expXml, result);
*/
}
@Test
public void testToBean() {
String orgXml =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"+
"<person>"+
"<name>donald</name>"+
"<age></age>"+
"<birthDay>2017-12-06 21:50:25</birthDay>"+
"<stature></stature>"+
"</person>";
Person person = XmlUtil.toBean(orgXml, Person.class);
log.info("to bean person:\n{}",XmlUtil.toXmlWithHeader(person));
}
}
运行测试类,控制输出
[INFO ] 2017-12-06 21:58:53 util.XmlUtilTests person to xml:
<?xml version="1.0" encoding="utf-8"?>
<person>
<name>donald</name>
<age>23</age>
<birthDay>2017-12-06 21:58:53</birthDay>
<stature>178.6</stature>
</person>
[INFO ] 2017-12-06 21:58:53 util.XmlUtilTests to bean person:
<?xml version="1.0" encoding="utf-8"?>
<person>
<name>donald</name>
<birthDay>2017-12-06 21:50:25</birthDay>
</person>
至此,xml工具类测试完毕。
附
单元测试和日志组件jar maven依赖:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>