对于网上的各种将XML同对象相互转换的方法比较多,但是如果对方给你提供的XML并非标准的XML格式,恐怕就只能自己封装方法了。作者在调用某票务公司提供的接口时,由于其返回XML流存在不标准的结构,因此自己写了个超类,继承于这个超类的所有对象可以调用toXml()、toObject(String xml)实现互转。
通过反射技术实现,如有不足之处,欢迎批评指正,非常感谢!
注意:
1、转换对象的属包含常见类型,如超类未涉及,请自行添加。
List/Integer(int)/String/Double(double)/Float(float)/Date/Long(long)/Short(short)/Byte(byte)/该类的子类
2、代码中的一些“未知”类
(1)ServiceException:自定义的运行时异常,请自行添加
(2)DateUtil:时间格式化工具,请自行封装
(3)StringUtil:字符串工具,使用到的方法:判断Object是否为空
3、需引入的包
<!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
4、代码中存在与作者接受的业务相关内容,请自行处理(去掉红框中的判断逻辑)
上代码:
1、接口
/**
* 实现此接口的对象可以和XML相互转换
* @author WolfShadow
* @date 2018年11月19日
*/
public interface Convertible {
String toXml();
<T> T toObject(String xml);
}
2、超类
import java.io.StringReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
//TODO 请自行添加这几个类或使用其他方式取代
import com.***.ServiceException;
import com.***.DateUtil;
import com.***.StringUtil;
import com.***.Convertible;
/**
* 可与XML相互转换的超类
* 此类的所有子类可实现与XML互相转换
* 终极优化:子类不再需要重写该2方法,转换对象的属包含常见类型,如超类未涉及,请自行添加
* List/Integer(int)/String/Double(double)/Float(float)/Date/Long(long)/Short(short)/Byte(byte)/该类的子类
*
* @author WolfShadow
* @date 2018年11月15日
*/
public class ConvertibleObject implements Convertible{
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
/**
* 通用将当前对象(this)转换成XML串,不包含<?xml version="1.0" encoding="UTF-8"?>
* @return
* @auther WolfShadow
* @date 2018年11月30日
*/
@Override
public String toXml() {
StringBuffer sBuffer = new StringBuffer();
Class<? extends ConvertibleObject> class1 = this.getClass();
/**
* 按照**提供的XML文档规范,类名首字母小写作为一个节点
*/
String name = class1.getSimpleName();
name = name.replaceFirst(".", name.substring(0, 1).toLowerCase());
sBuffer.append("<").append(name).append(">");
Field[] fields = class1.getDeclaredFields();
String fieldName = null;
Object value = null;
try {
for(Field field : fields){//通过反射机制遍历类的所有属性及值
fieldName = field.getName();
Class<?> type = field.getType();
field.setAccessible(true);
if(type.equals(List.class)){//1.List属性
// 判断List的成员是否是Convertible实现类
ParameterizedType genericType = (ParameterizedType)field.getGenericType();
Type[] arguments = genericType.getActualTypeArguments();
Class<?> class2 = Class.forName(arguments[0].toString().replaceFirst("class ", ""));
Class<?> superclass = class2.getSuperclass();
if (superclass != null && superclass.equals(ConvertibleObject.class)) {//子类
List<ConvertibleObject> list = (List<ConvertibleObject>)field.get(this);
if (list == null) {
continue;
}
sBuffer.append("<").append(fieldName).append(">");
for(ConvertibleObject object : list){
if (object != null) {
sBuffer.append(object.toXml());
}
}
}else {
sBuffer.append(this.getFieldValue(field,class2));
}
sBuffer.append("</").append(fieldName).append(">");
}else {
//2.ConvertibleObject 子类
Class<?> superclass = type.getSuperclass();
if (superclass != null && superclass.equals(ConvertibleObject.class)) {//子类
ConvertibleObject object = (ConvertibleObject)field.get(this);
if (object != null) {
sBuffer.append(object.toXml());
}
}else{//3.其他简单类型
value = this.getFieldValue(field,type);//统一获取属性值的方法
if (value == null) {
continue;
}
sBuffer.append("<").append(fieldName).append(">");
sBuffer.append(value);
sBuffer.append("</").append(fieldName).append(">");
}
}
}
} catch (Exception e) {
throw new ServiceException("class: "+this+" 转换成XML字符串时出错!");
}
sBuffer.append("</").append(name).append(">");
return sBuffer.toString();
}
/**
* 将XML转换成当前对象(this)
* @param xml
* @return
* @auther WolfShadow
* @date 2018年11月30日
*/
@Override
public <T> T toObject(String xml) {
if (!this.verifyXML(xml)) {
return null;
}
Class<? extends ConvertibleObject> class1 = this.getClass();
/**
* 按照**提供的XML文档规范,类名首字母小写作为一个节点
*/
String name = class1.getSimpleName();
name = name.replaceFirst(".", name.substring(0, 1).toLowerCase());
Field[] fields = class1.getDeclaredFields();
String fieldName = null;
String value = null;
SAXReader reader = new SAXReader();
Document document = null;
try {
document = reader.read(new StringReader(xml));//读取XML流为Document
Element root = document.getRootElement();
Element fieldE = null;
for(Field field : fields){//通过反射机制遍历类的所有属性
fieldName = field.getName();
Class<?> type = field.getType();
field.setAccessible(true);
if(type.equals(List.class)){//1.List属性
// 本可通用的方法,但因为**返回的XML存在不统一的情况,因此对products属性对应的XML需要特殊处理
List<Element> elements = null;
if ("products".equals(fieldName)) {
elements = root.elements("product");
}else{
Element element = root.element(fieldName);
elements = element.elements();
}
if (elements == null || elements.size() < 1) {
continue;
}
// 判断List的成员是否是Convertible实现类
ParameterizedType genericType = (ParameterizedType)field.getGenericType();
Type[] arguments = genericType.getActualTypeArguments();
Class<?> class2 = Class.forName(arguments[0].toString().replaceFirst("class ", ""));
Class<?> superclass = class2.getSuperclass();
List<Object> list = new ArrayList<>();
if (superclass != null && superclass.equals(ConvertibleObject.class)) {//子类
Object instance = class2.newInstance();
Method method = class2.getMethod("toObject", String.class);
Method method2 = class2.getMethod("isEmpty");
Object invoke = null;
for(Element e : elements){
instance = method.invoke(instance, e.asXML());
invoke = method2.invoke(instance);
if (invoke != null && Boolean.FALSE.equals(invoke)) {
list.add(instance);
}
}
}else{
for(Element e : elements){
list.add(e.getStringValue());
}
}
field.set(this, list);
}else {
fieldE = root.element(fieldName);
if (fieldE == null) {
continue;
}
//2.ConvertibleObject子类
Class<?> superclass = type.getSuperclass();
if (superclass != null && superclass.equals(ConvertibleObject.class)) {//子类
Object instance = type.newInstance();
Method method = type.getMethod("toObject", String.class);
Method method2 = type.getMethod("isEmpty");
Object invoke = null;
instance = method.invoke(instance, fieldE.asXML());
invoke = method2.invoke(instance);
if (invoke != null && Boolean.FALSE.equals(invoke)) {
field.set(this, instance);
}
}else{//其他普通类型
value = fieldE.getStringValue();
if (StringUtil.isNullOrEmpty(value)) {
continue;
}
this.setField(field, value, type);
}
}
}
return (T) this;//强制转换成其子类
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
throw new ServiceException("class: "+this+" 转换XML字符串为对象时出错!");
}
}
/**
* 常用类型属性值的设置
* @param field
* @param valueObj
* @auther WolfShadow
* @date 2018年11月22日
*/
public void setField(Field field, String value, Class<?> classType){
//field.setAccessible(true);
try {
if (classType.equals(Integer.class) || classType.equals(int.class)) {
field.set(this, Integer.parseInt(value));
}else if (classType.equals(Long.class) || classType.equals(long.class)) {
field.set(this, Long.parseLong(value));
}else if (classType.equals(Short.class) || classType.equals(short.class)) {
field.set(this, Short.parseShort(value));
}else if (classType.equals(Float.class) || classType.equals(float.class)) {
field.set(this, Float.parseFloat(value));
}else if (classType.equals(Double.class) || classType.equals(double.class)) {
field.set(this, Double.parseDouble(value));
}else if (classType.equals(Byte.class) || classType.equals(byte.class)) {
field.set(this, Byte.parseByte(value));
}else if (classType.equals(String.class)) {
field.set(this, value.toString());
}else if (classType.equals(Date.class)) {
int length = value.length();
if (length == 10) {//YYYY-MM-dd
value += " 00:00:00";
}else if (length == 16) {//YYYY-MM-dd HH:mm
value += ":00";
}else {
}
field.set(this, DateUtil.str2Date(value));
}else if (classType.equals(Boolean.class) || classType.equals(boolean.class)) {
field.set(this, Boolean.parseBoolean(value));
}else if (classType.equals(Object.class)) {
field.set(this, value);
}else {//如果未考虑到的属性类型,这里抛出异常
throw new ServiceException("基础置换类不支持此种属性的转换");
}
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
throw new ServiceException("class: "+this+" 转换XML字符串为对象时出错!");
}
}
/**
* 常用类型属性值的获取
* @param field
* @param valueObj
* @auther WolfShadow
* @date 2018年11月22日
*/
public String getFieldValue(Field field, Class<?> type){
//field.setAccessible(true);
try {
Object valueObj = field.get(this);
if (StringUtil.isNullOrEmpty(valueObj)) {
return null;
}
if (type.equals(Integer.class) || type.equals(int.class)) {
}else if (type.equals(Long.class) || type.equals(long.class)) {
}else if (type.equals(Short.class) || type.equals(short.class)) {
}else if (type.equals(Float.class) || type.equals(float.class)) {
}else if (type.equals(Double.class) || type.equals(double.class)) {
}else if (type.equals(Byte.class) || type.equals(byte.class)) {
}else if (type.equals(String.class)) {
}else if (type.equals(Date.class)) {
//只处理时间格式
return DateUtil.date2Str((Date)valueObj);
}else if (type.equals(Boolean.class) || type.equals(boolean.class)) {
}else if (type.equals(Object.class)) {
}else {//如果未考虑到的属性类型,这里抛出异常
//throw new ServiceException("基础置换类不支持此种属性的转换");
}
return valueObj.toString();
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
throw new ServiceException("class: "+this+" 转换XML字符串为对象时出错!");
}
}
/**
* 默认情况下不需要做XML判断,但是在解析原始(标准)XML时需要重写该方法判断解析的XML是否合法
* @param xml
* @return
* @auther WolfShadow
* @date 2018年11月23日
*/
public boolean verifyXML(String xml){
//return xml.startsWith(xmlheader) || xml.startsWith(xmlheader_low);
return true;
}
/**
* 对象为空
* @return
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @auther WolfShadow
* @date 2018年11月23日
*/
public boolean isEmpty() throws IllegalArgumentException, IllegalAccessException{
Class<? extends ConvertibleObject> class1 = this.getClass();
Field[] fields = class1.getDeclaredFields();
Object object = null;
for(Field field : fields){
try {
field.setAccessible(true);
object = field.get(this);
} catch (Exception e) {
object = null;
}
if (!StringUtil.isNullOrEmpty(object)) {
return false;
}
}
return true;
}
}