使用反射技术,实现XML与对象相互转换(自己封装的超类,相互学习)

2024-05-08 17:58

本文主要是介绍使用反射技术,实现XML与对象相互转换(自己封装的超类,相互学习),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

       对于网上的各种将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{@Overridepublic 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日*/@Overridepublic 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日*/@Overridepublic <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流为DocumentElement 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-ddvalue += " 00:00:00";}else if (length == 16) {//YYYY-MM-dd HH:mmvalue += ":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;}
}

 

 

      

这篇关于使用反射技术,实现XML与对象相互转换(自己封装的超类,相互学习)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/971028

相关文章

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

prometheus如何使用pushgateway监控网路丢包

《prometheus如何使用pushgateway监控网路丢包》:本文主要介绍prometheus如何使用pushgateway监控网路丢包问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录监控网路丢包脚本数据图表总结监控网路丢包脚本[root@gtcq-gt-monitor-prome

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期