最近在做一个银企直连的项目,因为接受到的数据为XML格式的String。且几乎每一次接收到的XML文件格式以及内容基本都不一样。所以目前在dom4j的基础上,加上对反射的应用,直接把XML数据转为(固定格式,内部变量可多不可少的)实体类。这样方便了对XML数据的选择应用以及对XML数据的后续处理。
准备环境
1.引入相关Maven依赖
<!-- dom4j处理XML数据 -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6</version>
</dependency>
<!-- 引入lombak插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2.准备好需要解析的字符串(方便测试)
String xmlString=
"<?xml version=\"1.0\" encoding=\"GB2312\"?>" +
"<CMBC trnCode=\"Xfer\" security=\"none\" lang=\"chs\" header=\"100\" version=\"100\" >" +
"<responseHeader>" +
"<status>" +
"<code>0</code>" +
"<severity>info</severity>" +
"<message>ok</message>" +
"</status>" +
"<dtServer>2020-08-24 09:38:32</dtServer>" +
"<userKey>N</userKey>" +
"<dtDead></dtDead>" +
"<language>chs</language>" +
"</responseHeader>" +
"<xDataBody>" +
"<transfer>" +
"<trnId>zhouyf</trnId>" +
"<svrId>31301202008246046584784313000000</svrId>" +
"<insId>00001</insId>" +
"<balance>59289497.37</balance>" +
"</transfer>" +
"</xDataBody>" +
"</CMBC>";
ps: 不需要XML头数据,直接从CMBC标签开始解析。(为了方便看专门进行了缩进)可以看到这个XML标签有4级。如:CMBC->responseHeader->status->code.
直接开始解析XML
话不多说,直接代码
// 创建SAXReader的对象reader
SAXReader reader = new SAXReader();
try {
// 通过reader对象的read方法加载books.xml文件,获取docuemnt对象。
Document document = reader.read(new ByteArrayInputStream(xmlString.getBytes(StandardCharsets.UTF_8)));
// 通过document对象获取根节点bookstore
Element oneEle = document.getRootElement();
List<Attribute> cmbcAttrs = oneEle.attributes();
//组装根节点中的参数
for (Attribute attr : cmbcAttrs) {
System.out.println("[attr : "+oneEle.getName()+"] "+attr.getName()+" : "+attr.getValue());
}
//剩余的不用组装attr参数 一共有四级
//【第二级】
Iterator twoDocument = oneEle.elementIterator();
//twoDocument.hasNext()判断该标签是否拥有子标签,如果有就继续解析
if(twoDocument.hasNext()) {
while (twoDocument.hasNext()) {
Element twoEle = (Element) twoDocument.next();
//【第三级】
Iterator threeDocument = twoEle.elementIterator();
if (threeDocument.hasNext()) {
while (threeDocument.hasNext()) {
Element threeEle = (Element) threeDocument.next();
//【第四级】
Iterator fourDocument = threeEle.elementIterator();
if (fourDocument.hasNext()) {
while (fourDocument.hasNext()) {
Element fourEle = (Element) fourDocument.next();
System.out.println("[ele : "+fourEle.getParent().getName()+"] "+ fourEle.getName() + " :" + fourEle.getStringValue());
}
} else System.out.println("[ele : "+threeEle.getParent().getName()+"] "+ threeEle.getName() + " :" + threeEle.getStringValue());
}
} else System.out.println("[ele : "+twoEle.getParent().getName()+"] "+ twoEle.getName() + " :" + twoEle.getStringValue());
}
}else System.out.println("[ele : "+oneEle.getParent().getName()+"] "+ oneEle.getName() + " :" + oneEle.getStringValue());
} catch (DocumentException e) {
e.printStackTrace();
}
输出结果:
[attr : CMBC] trnCode : Xfer
[attr : CMBC] security : none
[attr : CMBC] lang : chs
[attr : CMBC] header : 100
[attr : CMBC] version : 100
[ele : status] code :0
[ele : status] severity :info
[ele : status] message :ok
[ele : responseHeader] dtServer :2020-08-24 09:38:32
[ele : responseHeader] userKey :N
[ele : responseHeader] dtDead :
[ele : responseHeader] language :chs
[ele : transfer] trnId :zhouyf
[ele : transfer] svrId :31301202008246046584784313000000
[ele : transfer] insId :00001
[ele : transfer] balance :59289497.37
可以看到XML文件中的内容已经全部被解析出来了,但是这还不是我想要的。因为目前最多可以把这些数据封装成map格式,但是对动态的去引用的时候还是很不方便。所以请往下看…
通过反射,把XMl解析到实体类中
1.建立对应的实体类(实体类中的变量可多不可少,我直接建的内部类。为了演示特意多建上个多余的字段)
@Data
public class CMBC {
/**
* 交易请求代码
*/
private String trnCode;
/**
* 安全信息
*/
private String security;
/**
* 语言
*/
private String lang;
/**
* 请求头信息
*/
private String header;
/**
* 版本信息
*/
private String version;
/**
* 响应头
*/
private ResponseHeader responseHeader;
/**
* 返回数据
*/
private XDataBody xDataBody;
/**
* 多余的字段
*/
private String moreString;
/**
* 新建的时候初始化期内部类,不然后面通过反射来实例化对象的时候可能出现问题
*/
public CMBC(){
this.responseHeader = this.new ResponseHeader();
this.xDataBody = this.new XDataBody();
}
@Data
public class ResponseHeader{
private Status status;
private String dtServer;
private String userKey;
private String dtDead;
private String language;
/**
* 多余的字段
*/
private String moreString;
public ResponseHeader(){
this.status = this.new Status();
}
/**
* 交易状态码
*/
@Data
public class Status{
private String code;
private String severity;
private String message;
/**
* 多余的字段
*/
private String moreString;
}
}
@Data
public class XDataBody{
/**
* 转账详情
*/
private Transfer transfer;
public XDataBody(){
this.transfer = this.new Transfer();
}
@Data
public class Transfer{
/**
* 客户端交易的唯一标志
*/
private String trnId;
/**
* 服务器该笔交易的标识
*/
private String svrId;
/**
* 指令 ID,请求时给出的 ID
*/
private String insId;
/**
* 余额
*/
private String balance;
}
}
}
2.在XML解析中添加对反射的引用,直接解析为实体对象
CMBC cmbc = new CMBC();
// 创建SAXReader的对象reader
SAXReader reader = new SAXReader();
try {
// 通过reader对象的read方法加载books.xml文件,获取docuemnt对象。
Document document = reader.read(new ByteArrayInputStream(xmlString.getBytes(StandardCharsets.UTF_8)));
// 通过document对象获取根节点bookstore
Element oneEle = document.getRootElement();
List<Attribute> cmbcAttrs = oneEle.attributes();
//组装CMBC根节点中的参数
for (Attribute attr : cmbcAttrs) {
String methodName = "set"+attr.getName().substring(0, 1).toUpperCase() +
attr.getName().substring(1);
Method m= cmbc.getClass().getMethod(methodName,String.class);
//3 通过方法的反射操作方法
m.invoke(cmbc,attr.getValue());
}
//剩余的不用组装attr参数 一共有四级
Iterator twoDocument = oneEle.elementIterator();
if(twoDocument.hasNext()) {
while (twoDocument.hasNext()) {
Element twoEle = (Element) twoDocument.next();
Iterator threeDocument = twoEle.elementIterator();
if (threeDocument.hasNext()) {
while (threeDocument.hasNext()) {
Element threeEle = (Element) threeDocument.next();
//反射出二级对象
String twoName = threeEle.getParent().getName();
//反射数据
Method getThreeClassMethod = cmbc.getClass().getMethod("get"+twoName.substring(0, 1).toUpperCase()+twoName.substring(1));
Object objectThreeClass = getThreeClassMethod.invoke(cmbc);
Iterator fourDocument = threeEle.elementIterator();
if (fourDocument.hasNext()) {
//表示还有子节点
while (fourDocument.hasNext()) {
Element fourEle = (Element) fourDocument.next();
//获取三级对象
String threeName = fourEle.getParent().getName();
Method getFourClassMethod = objectThreeClass.getClass().getMethod("get"+threeName.substring(0, 1).toUpperCase()+threeName.substring(1));
Object objectFourClass = getFourClassMethod.invoke(objectThreeClass);
//把值反射到对象中
String methodName = "set"+fourEle.getName().substring(0, 1).toUpperCase() +
fourEle.getName().substring(1);
Method m= objectFourClass.getClass().getMethod(methodName,String.class);
//通过方法的反射操作方法
m.invoke(objectFourClass,fourEle.getStringValue());
}
} else {
//没有子节点
String methodName = "set"+threeEle.getName().substring(0, 1).toUpperCase() +
threeEle.getName().substring(1);
Method m= objectThreeClass.getClass().getMethod(methodName,String.class);
//3 通过方法的反射操作方法
m.invoke(objectThreeClass,threeEle.getStringValue());
}
}
} else {
String methodName = "set"+twoEle.getName().substring(0, 1).toUpperCase() +
twoEle.getName().substring(1);
Method m= cmbc.getClass().getMethod(methodName,String.class);
//3 通过方法的反射操作方法
m.invoke(cmbc,twoEle.getStringValue());
}
}
}
System.out.println(cmbc);
} catch (DocumentException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
输出结果:
CMBC(trnCode=Xfer, security=none, lang=chs, header=100, version=100, responseHeader=CMBC.ResponseHeader(status=CMBC.ResponseHeader.Status(code=0, severity=info, message=ok, moreString=null), dtServer=2020-08-24 09:38:32, userKey=N, dtDead=, language=chs, moreString=null), xDataBody=CMBC.XDataBody(transfer=CMBC.XDataBody.Transfer(trnId=zhouyf, svrId=31301202008246046584784313000000, insId=00001, balance=59289497.37)), moreString=null)
可以看到XML已经被成功的解析到实体中了。