dom4j+反射实现bean与xml的相互转换
由于目前在工作中一直用的dom4j+反射实现bean与xml的相互转换,记录一下,如果有不正确的地方欢迎大家指正~~~
一、反射机制
在此工具类中使用到了反射技术,所以提前也看了一些知识点,例如:http://www.51gjie.com/java/82(这里面有大量的例子可以学习)
二、dom4j
dom4j会将整个xml加载至内存,并解析成一个document对象,但是可能会造成内存溢出现象。
Document:表示整个xml文档。文档Document对象是通常被称为DOM树。
Element:表示一个xml元素。Element对象有方法来操作其子元素,它的文本,属性和名称空间
Attribute:表示元素的属性。属性有方法来获取和设置属性的值。它有父节点和属性类型。
Node:代表元素,属性或者处理指令。
三、dom4j读取xml
读取xml文档主要依赖于org.dom4j.io包,翻看其中源码可以看出提供DOMReader、SaxReader、XPPReader、XPP3Reader,我这里主要查看了SaxReader源码,其他的没有深入看过,所以使用SaxReader。
/**
* 将xml字符串转换为Document对象
* @param xml
* @return
*/
public Document getDocumentByString(String xml)
{
//1.字符串输入流
StringReader stringReader = new StringReader(xml);
//2.获取解析器
SAXReader saxReader = new SAXReader();
//3.声明document对象
Document document = null;
try
{
//4.解析xml,生成document对象
document = saxReader.read(stringReader);
}
catch (DocumentException e)
{
log.error("xml解析失败",e);
}
return document;
}
四、bean与xml的互转方法
我这里测试案例,查询学生学校信息,返回学校及多个学生信息。如下准备:
1.准备bean
请求实体bean:ReqSchool.java
public class ReqSchool {
//学校编号
private String number;
//学校名称
private String name;
//学校省份
private String province;
//学校地址
private String address;
//学生班级
private String stuclass;
//学生姓名
private String stuname;
//学生分数
private String stuscore;
//省略set和get方法
}
响应实体bean:RspSchool.java
public class RspSchool {
//学校编号
private String number;
//学校名称
private String name;
//学校省份
private String province;
//学校地址
private String address;
//多个学生
private List<Student> students; //模拟测试数据,返回多个学生
//省略get和set方法
}
响应实体bean的泛型:RspStudent.java
public class RspStudent {
//学生班级
private String stuclass;
//学生姓名
private String stuname;
//学生分数
private String stuscore;
//省略set和get方法
}
2.准备xml
①请求模版requestXML
这里以${元素名}作为请求模版,也可以修改工具类进行改造。
<?xml version="1.0" encoding = "GBK"?>
<SCHOOL>
<Head>
<number>${number}</number>
<name>${name}</name>
<province>${province}</province>
<address>${address}</address>
</Head>
<Body>
<stuclass>${stuclass}</stuclass>
<stuname>${stuname}</stuname>
<stuscore>${stuscore}</stuscore>
</Body>
</SCHOOL>
②响应responseXML
<?xml version="1.0" encoding = "GBK"?>
<SCHOOL>
<Head>
<number>0001</number>
<name>xxx实验小学</name>
<province>北京市</province>
<address>西城区</address>
</Head>
<Body>
<students>
<student>
<stuclass>高三二班</stuclass>
<stuname>李四</stuname>
<stuscore>100</stuscore>
</student>
<student>
<stuclass>高三三班</stuclass>
<stuname>张三</stuname>
<stuscore>95</stuscore>
</student>
<student>
<stuclass>高三四班</stuclass>
<stuname>王五</stuname>
<stuscore>0</stuscore>
</student>
</students>
</Body>
</SCHOOL>
3.工具类(可直接复制粘贴使用)
复制粘贴使用时,需保证和我这里的请求报文模版相同(即使用${元素名}),当然也可手动改造此方法。
@Slf4j
public class XmlUtil {
//${abc}正则
public static String varRegex = "\\$\\{\\s*(\\w+)\\s*(([\\+\\-])\\s*(\\d+)\\s*)?\\}";
/**
* xml解析成document对象
*
* @param xml
* @return
*/
public Document getDocument(String xml) {
StringReader stringReader = new StringReader(xml); SAXReader saxReader = new SAXReader(); Document document = null; try {
document = saxReader.read(stringReader);
} catch (DocumentException e) { }
return document;
} /**
* xml与bean的相互转换
*
* @param element
* @param direction 1:java2xml,2:xml2java
* @param obj
*/
public void parseXml(Element element, String direction, Object obj) {
//获取当前元素的所有子节点(在此我传入根元素)
List<Element> elements = element.elements();
//判断是否有子节点
if (elements != null && elements.size() > 0)
{
//进入if说明有子节点
//遍历
for (Element e : elements)
{
//判断转换方向(1:java2xml;2:xml2java)
if ("2".equals(direction)) //这里是xml转bean
{
//声明Field
Field field = null;
try
{
//反射获取属性
field = obj.getClass().getDeclaredField(e.getName());
} catch (Exception e1)
{ }
//获取当前属性是否为list
if (field!=null&&List.class.getName().equals(field.getType().getName()))
{
//反射获取set方法
Method method = this.getDeclaredMethod(obj, "set".concat(this.toUpperCaseFirstOne(e.getName())), new Class[]{List.class});
//声明临时list
List temList = new ArrayList();
if (method!=null)
{
try
{
//反射调用obj的当前方法,可变参数为templist
method.invoke(obj, temList);
} catch (Exception e1) {
log.info("【{}】方法执行失败",method,e1);
}
}
//获取List的泛型参数类型
Type gType = field.getGenericType();
//判断当前类型是否为参数化泛型
if (gType instanceof ParameterizedType)
{
//转换成ParameterizedType对象
ParameterizedType pType = (ParameterizedType) gType;
//获得泛型类型的泛型参数(实际类型参数)
Type[] tArgs = pType.getActualTypeArguments();
if (tArgs!=null&&tArgs.length>0)
{
//获取当前元素的所有子元素
List<Element> elementSubList=e.elements();
//遍历
for (Element e1:elementSubList) {
try
{
//反射创建对象
Object tempObj = Class.forName(tArgs[0].getTypeName()).newInstance();
temList.add(tempObj);
//递归调用自身
this.parseXml(e1, direction, tempObj);
} catch (Exception e2)
{
log.error("【{}】对象构造失败",tArgs[0].getTypeName(),e2);
}
} }
}
}
else
{
//说明不是list标签,继续递归调用自身即可
this.parseXml(e, direction, obj);
}
}
else if("1".equals(direction)) //说明转换方向为:javabean转xml
{
//递归调用自身
this.parseXml(e, direction, obj);
}
//此时还在for循环遍历根元素的所有子元素
}
}
else
{
//说明无子节点
//获取当前元素的名称
String nodeName = element.getName();
//获取当前元素的对应的值
String nodeValue = element.getStringValue(); //判断转换方向:1:java2xml、2:xml2java
if ("1".equals(direction))//java2xml
{
if (nodeValue != null && nodeValue.matches(varRegex))
{
/**
* 获取模板中各节点定义的变量名,例如<traceNo>${traceNo}</traceNo>
*/
nodeValue = nodeValue.substring(nodeValue.indexOf("${") + 2, nodeValue.indexOf("}")); Object value = null;
//根据解析出的变量名,调用obj对象的getXXX()方法获取变量值
Method method = this.getDeclaredMethod(obj, "get".concat(this.toUpperCaseFirstOne(nodeValue)), null);
if (method != null) {
try {
value = method.invoke(obj);
} catch (Exception e) {
log.error("方法【{}】调用异常", "get".concat(this.toUpperCaseFirstOne(nodeValue)));
}
}
//将变量值填充至xml模板变量名位置,例如<traceNo>${traceNo}</traceNo>
element.setText(value == null ? "" : value.toString());
}
//叶子节点
log.debug("节点名【{}】,节点变量名【{}】",element.getName(),nodeValue);
}
else if ("2".equals(direction))//xml2java
{
if (nodeName != null && !"".equals(nodeName))
{
//根据xml节点名,调用obj对象的setXXX()方法为obj设置变量值
Method method = this.getDeclaredMethod(obj, "set".concat(this.toUpperCaseFirstOne(nodeName)), new Class[]{String.class});
if(method!=null)
{
try
{
method.invoke(obj, nodeValue);
} catch (Exception e)
{
log.error("方法【{}】调用异常","set".concat(this.toUpperCaseFirstOne(nodeName)));
}
}
}
}
}
} private Method getDeclaredMethod(Object object, String methodName, Class<?>[] parameterTypes)
{ for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
{
try
{
return superClass.getDeclaredMethod(methodName, parameterTypes);
}
catch (NoSuchMethodException e)
{
//Method 不在当前类定义, 继续向上转型
}
//..
} return null;
}
private String toUpperCaseFirstOne(String s)
{
// 进行字母的ascii编码前移,效率要高于截取字符串进行转换的操作
char[] cs = s.toCharArray();
cs[0] -= 32;
return String.valueOf(cs);
}
}
4.测试
1.准备请求实体bean
private static ReqSchool makeReq() {
ReqSchool rspSchool = new ReqSchool();
//学校编号
rspSchool.setNumber("1001");
//学校名称
rspSchool.setName("实验小学");
//学校省份
rspSchool.setProvince("北京市");
//学校地区
rspSchool.setAddress("西城区");
//学生班级
rspSchool.setStuclass("高一(2)班");
//学生姓名
rspSchool.setStuname("张三");
//学生成绩
rspSchool.setStuscore("92");
return rspSchool;
}
2.main方法测试(请求)
public static void main(String[] args)
{
//定义请求模版
String requestXml="<?xml version=\"1.0\" encoding = \"GBK\"?>\n" +
"<SCHOOL>\n" +
"<Head>\n" +
"<number>${number}</number>\n" +
"<name>${name}</name>\n" +
"<province>${province}</province>\n" +
"<address>${address}</address>\n" +
"</Head>\n" +
"<Body>\n" +
"<stuclass>${stuclass}</stuclass>\n" +
"<stuname>${stuname}</stuname>\n" +
"<stuscore>${stuscore}</stuscore>\n" +
"</Body>\n" +
"</SCHOOL>";
//这里我直接使用构造方法(实际开发应以线程安全的单例模式)
XmlUtil xmlUtil = new XmlUtil();
//获取document对象
Document document = xmlUtil.getDocument(requestXml);
//获取根元素
Element root = document.getRootElement();
//请求实体bean
ReqSchool reqSchool = makeReq();
//解析xml,1:表示java2xml
xmlUtil.parseXml(root,"1",reqSchool);
//输出请求报文
System.out.println(root.asXML());
}
查看控制台结果:
3.main方法测试(响应)
public static void main(String[] args)
{ //定义响应报文
String responseXML="<?xml version=\"1.0\" encoding = \"GBK\"?>\n" +
"<SCHOOL>\n" +
"<Head>\n" +
"<number>0001</number>\n" +
"<name>xxx实验小学</name>\n" +
"<province>北京市</province>\n" +
"<address>西城区</address>\n" +
"</Head>\n" +
"<Body>\n" +
" <students>\n" +
" <student>\n" +
" <stuclass>高三二班</stuclass>\n" +
" <stuname>李四</stuname>\n" +
" <stuscore>100</stuscore>\n" +
" </student>\n" +
" <student>\n" +
" <stuclass>高三三班</stuclass>\n" +
" <stuname>张三</stuname>\n" +
" <stuscore>95</stuscore>\n" +
" </student>\n" +
" <student>\n" +
" <stuclass>高三四班</stuclass>\n" +
" <stuname>王五</stuname>\n" +
" <stuscore>0</stuscore>\n" +
" </student>\n" +
" </students>\n" +
"</Body>\n" +
"</SCHOOL>";
//这里我直接使用构造方法(实际开发应以线程安全的单例模式)
XmlUtil xmlUtil = new XmlUtil();
Document document = xmlUtil.getDocument(responseXML);
Element rootElement = document.getRootElement();
RspSchool rspSchool = new RspSchool();
xmlUtil.parseXml(rootElement,"2",rspSchool);
System.out.println(rspSchool); }
控制结果如下:
五、总结
1.dom4j解析xml的步骤
①获取执行xml的输入流
②创建xml读取对象(SaxReader),用于读取输入流
③通过读取对象(SaxReader)读取xml的输入流,获取文档对象(Document)
④通过文档对象,得到整个文档的 根元素对象(Element)
⑤通过根元素,得到其他层次的所有元素对象
2.反射
反射这里是重中之重,感谢大家的阅读,如有问题,欢迎大家指正~
dom4j+反射实现bean与xml的相互转换的更多相关文章
- JAVA Bean和XML之间的相互转换 - XStream简单入门
JAVA Bean和XML之间的相互转换 - XStream简单入门 背景介绍 XStream的简介 注解简介 应用实例 背景介绍 我们在工作中经常 遇到文件解析为数据或者数据转化为xml文件的情况, ...
- JAVA bean与XML互转的利器---XStream
最近在项目中遇到了JAVA bean 和XML互转的需求, 本来准备循规蹈矩使用dom4j忽然想起来之前曾接触过的XStream, 一番研究豁然开朗,利器啊利器, 下来就XStream的一些用法与大家 ...
- java学习:用反射构造bean
先贴一些反射的基本知识:-------------------------------------------------------------------- 一.什么是反射:反射的概念是由Smit ...
- 使用JAXB实现Bean与Xml相互转换
最近几天,我自己负责的应用这边引入了一个新的合作方,主要是我这边调用他们的接口,但是有个很坑的地方,他们传参居然不支持json格式,并且只支持xml格式进行交互,于是自己写了一个工具类去支持bean与 ...
- 菜鸟学Java(八)——dom4j详解之读取XML文件
dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的.dom4j是一个非常非常优秀的Java XML API,具有性能优异.功能强大和极端易用使用的特点,同时它也是一个开放源 ...
- dom4j解析带命名空间的xml文件
文件内容如下 <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd=& ...
- Spring基础使用(一)--------IOC、Bean的XML方式装配
基础 1.xml文件基础格式: <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns=&q ...
- Java反射之Bean修改更新属性值等工具类
package com.bocean.util; import java.lang.annotation.Annotation; import java.lang.reflect.Field; imp ...
- 利用反射与dom4j读取javabean生成对应XML和读取XML得到对应的javabean对象集合
转自:http://blog.csdn.net/zhao19861029/article/details/8473245 首先实现生成对应的JAVAbean的XML文件方法 /** * DMO4J写入 ...
随机推荐
- (转)eclipse调试java程序的九个技巧
转自:http://www.cnblogs.com/lingiu/p/3802391.html 九个技巧: 逻辑结构 条件debug 异常断点 单步过滤 跳到帧 Inspect expressions ...
- SSM开发基于Java EE在线图书销售系统
SSM(Spring+Spring MVC+MyBatis)开发基于Java EE在线图书销售系统 网站成功建立和运行很大部分取决于网站开发前的规划,因此为了在网站建立过程中避免一些不 ...
- Python七夕记
- java List 排序,升序,降序
import java.util.*; public class EntrySets { public static void main(String[] args) { Map<Object, ...
- python解压压缩包的几种方式
这里讨论使用Python解压如下五种压缩文件: .gz .tar .tgz .zip .rar 简介 gz: 即gzip,通常只能压缩一个文件.与tar结合起来就可以实现先打包,再压缩. tar: ...
- util之ArrayList
Java之ArrayList 方法: add(o: E): void 在list的末尾添加一个元素o add(index: int, o: E): void 在指定的index处插入元素o clear ...
- OpenCV的视频输入和相似度测量
#include <iostream> #include <string> #include <iomanip> // 控制浮动类型的打印精度 #include & ...
- css实现移动端滚动条隐藏仍然可以滚动内容
.g-panel { height: calc(100% - 112px); overflow: auto; &::-webkit-scrollbar { display: none; // ...
- jenkins常用
记最精简版的启动:gradle集成springboot+vue 安装jenkins,安装待推荐插件 服务器安装gradle 服务器安装npm 配置git仓库,配置git开发者账号等 配置gradle构 ...
- fader
fader在音频处理中是比较基础的处理.通常用于平滑的调节音量,或是音频的渐入和渐出效果. 比较常见的fader有line和cubic线型的fader. line fader即fader的渐变过程是线 ...