使用反射技术,实现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

相关文章

利用Python实现Excel文件智能合并工具

《利用Python实现Excel文件智能合并工具》有时候,我们需要将多个Excel文件按照特定顺序合并成一个文件,这样可以更方便地进行后续的数据处理和分析,下面我们看看如何使用Python实现Exce... 目录运行结果为什么需要这个工具技术实现工具的核心功能代码解析使用示例工具优化与扩展有时候,我们需要将

使用Nginx配置文件服务器方式

《使用Nginx配置文件服务器方式》:本文主要介绍使用Nginx配置文件服务器方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 为什么选择 Nginx 作为文件服务器?2. 环境准备3. 配置 Nginx 文件服务器4. 将文件放入服务器目录5. 启动 N

使用nohup和--remove-source-files在后台运行rsync并记录日志方式

《使用nohup和--remove-source-files在后台运行rsync并记录日志方式》:本文主要介绍使用nohup和--remove-source-files在后台运行rsync并记录日... 目录一、什么是 --remove-source-files?二、示例命令三、命令详解1. nohup2.

Python+PyQt5实现文件夹结构映射工具

《Python+PyQt5实现文件夹结构映射工具》在日常工作中,我们经常需要对文件夹结构进行复制和备份,本文将带来一款基于PyQt5开发的文件夹结构映射工具,感兴趣的小伙伴可以跟随小编一起学习一下... 目录概述功能亮点展示效果软件使用步骤代码解析1. 主窗口设计(FolderCopyApp)2. 拖拽路径

Spring AI 实现 STDIO和SSE MCP Server的过程详解

《SpringAI实现STDIO和SSEMCPServer的过程详解》STDIO方式是基于进程间通信,MCPClient和MCPServer运行在同一主机,主要用于本地集成、命令行工具等场景... 目录Spring AI 实现 STDIO和SSE MCP Server1.新建Spring Boot项目2.a

Qt之QMessageBox的具体使用

《Qt之QMessageBox的具体使用》本文介绍Qt中QMessageBox类的使用,用于弹出提示、警告、错误等模态对话框,具有一定的参考价值,感兴趣的可以了解一下... 目录1.引言2.简单介绍3.常见函数4.按钮类型(QMessage::StandardButton)5.分步骤实现弹窗6.总结1.引言

Python使用Reflex构建现代Web应用的完全指南

《Python使用Reflex构建现代Web应用的完全指南》这篇文章为大家深入介绍了Reflex框架的设计理念,技术特性,项目结构,核心API,实际开发流程以及与其他框架的对比和部署建议,感兴趣的小伙... 目录什么是 ReFlex?为什么选择 Reflex?安装与环境配置构建你的第一个应用核心概念解析组件

Qt中Qfile类的使用

《Qt中Qfile类的使用》很多应用程序都具备操作文件的能力,包括对文件进行写入和读取,创建和删除文件,本文主要介绍了Qt中Qfile类的使用,具有一定的参考价值,感兴趣的可以了解一下... 目录1.引言2.QFile文件操作3.演示示例3.1实验一3.2实验二【演示 QFile 读写二进制文件的过程】4.

Python将字符串转换为小写字母的几种常用方法

《Python将字符串转换为小写字母的几种常用方法》:本文主要介绍Python中将字符串大写字母转小写的四种方法:lower()方法简洁高效,手动ASCII转换灵活可控,str.translate... 目录一、使用内置方法 lower()(最简单)二、手动遍历 + ASCII 码转换三、使用 str.tr

spring security 超详细使用教程及如何接入springboot、前后端分离

《springsecurity超详细使用教程及如何接入springboot、前后端分离》SpringSecurity是一个强大且可扩展的框架,用于保护Java应用程序,尤其是基于Spring的应用... 目录1、准备工作1.1 引入依赖1.2 用户认证的配置1.3 基本的配置1.4 常用配置2、加密1. 密