场景介绍
下面的xml模板是在项目中实际使用的。项目中所有的接口都具有相同的结构。
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:cbcc:std:caps:2020:tech:xsd:caps.202.001.01" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Message>
<Head>
<CorpNo>3310000001</CorpNo>
<ResFlag>SUCC</ResFlag>
<ErrorCode>I000</ErrorCode>
<ErrorMsg>平台检验成功</ErrorMsg>
</Head>
<Body>
<ReturnTime>2020-10-30T09:26:51</ReturnTime>
<SysSeqNo>2020103050006167</SysSeqNo>
<SerialNum>2020103000000001</SerialNum>
<RetCode>111111</RetCode>
<RetMsg>业务处理中</RetMsg>
<Remark>专项科研经费</Remark>
<Use>服务费202010</Use>
<BtchNb>50006167</BtchNb>
</Body>
</Message>
</Document>
所有接口结构统一,一个“Documen“根节点,下面一个“Message“子节点。“Message“下面两个子节点“Head“,“Body“,“Head“,“Body“下是具体的业务数据节点。
XStream工具类
导入XStream依赖
<!-- 解析xml -->
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.10</version>
</dependency>
<!-- fastjson, 展示解析结果 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.59</version>
</dependency>
XStreamXmlUtil.java代码
package com.xml.parse.utils;
import com.thoughtworks.xstream.XStream;
/**
* 解析xml工具
* @author pst19
*
*/
public class XStreamXmlUtil {
/**
* 文中的样例有两个子类节点Head,Body,每个接口是不同的。
* 根节点是Document
* @param xml 报文内容
* @param rootClass 根节点Class对象
* @param subClass1 业务子类节点1
* @param subClass2 业务子类节点2
* @return
*/
public static <T> T xmlToBean(String xml, Class<T> rootClass, Class<?> subClass1, Class<?> subClass2){
XStream xStream = new XStream();
XStream.setupDefaultSecurity(xStream);
xStream.allowTypesByRegExp(new String[] { ".*" });
//不使用默认的类加载器,需要手动设置类加载器
xStream.setClassLoader(rootClass.getClassLoader());
xStream.processAnnotations(new Class[]{rootClass, subClass1, subClass2});
//使用子类代替父类。这是本方法通用的关键点,解析不同报文传入不同的子类对象,
xStream.addDefaultImplementation(subClass1, subClass1.getSuperclass());
xStream.addDefaultImplementation(subClass2, subClass2.getSuperclass());
return (T)xStream.fromXML(xml);
}
}
通用实体对象
通过注解 完成xml节点和实体的层级映射
- 从根节点开始,首先是 Document
package com.xml.parse.entity;
import com.thoughtworks.xstream.annotations.XStreamAlias;
//映射根节点“Document”
@XStreamAlias("Document")
public class Document {
//映射根节点下子节点“Message”
@XStreamAlias("Message")
private Message message;
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
}
}
- Message节点实体对象
package com.xml.parse.entity;
import com.thoughtworks.xstream.annotations.XStreamAlias;
//映射"Message"节点
@XStreamAlias("Message")
public class Message {
//映射"Head"节点
@XStreamAlias("Head")
private BaseHead head;
//映射"Body"节点
@XStreamAlias("Body")
private BaseBody body;
public BaseHead getHead() {
return head;
}
public void setHead(BaseHead head) {
this.head = head;
}
public BaseBody getBody() {
return body;
}
public void setBody(BaseBody body) {
this.body = body;
}
}
- Head,Body的基础类,业务子类都要继承这两个类
Head
package com.xml.parse.entity;
public class BaseHead {
}
Body
package com.xml.parse.entity;
public class BaseBody {
}
创建业务实体类,用于保存实际的业务数据
以样例中的报文“caps.202.001.01”为例
Caps202Head类
package com.xml.parse.entity.caps202;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.xml.parse.entity.BaseHead;
/**
* 继承基础类BaseHead
*
*
*/
@XStreamAlias("Head")
public class Caps202Head extends BaseHead{
//映射CorpNo节点
//以下属性和节点一一对应
@XStreamAlias("CorpNo")
private String corpNo;
@XStreamAlias("ResFlag")
private String resFlag;
@XStreamAlias("ErrorCode")
private String errorCode;
@XStreamAlias("ErrorMsg")
private String errorMsg;
public String getCorpNo() {
return corpNo;
}
public void setCorpNo(String corpNo) {
this.corpNo = corpNo;
}
public String getResFlag() {
return resFlag;
}
public void setResFlag(String resFlag) {
this.resFlag = resFlag;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
Caps202Body类
package com.xml.parse.entity.caps202;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.xml.parse.entity.BaseBody;
@XStreamAlias("Body")
public class Caps202Body extends BaseBody{
//以下属性和节点一一对应
@XStreamAlias("ReturnTime")
private String returnTime;
@XStreamAlias("SysSeqNo")
private String sysSeqNo;
@XStreamAlias("SerialNum")
private String serialNum;
@XStreamAlias("RetCode")
private String retCode;
@XStreamAlias("RetMsg")
private String retMsg;
@XStreamAlias("Remark")
private String remark;
@XStreamAlias("Use")
private String use;
@XStreamAlias("BtchNb")
private String btchNb;
public String getReturnTime() {
return returnTime;
}
public void setReturnTime(String returnTime) {
this.returnTime = returnTime;
}
public String getSysSeqNo() {
return sysSeqNo;
}
public void setSysSeqNo(String sysSeqNo) {
this.sysSeqNo = sysSeqNo;
}
public String getSerialNum() {
return serialNum;
}
public void setSerialNum(String serialNum) {
this.serialNum = serialNum;
}
public String getRetCode() {
return retCode;
}
public void setRetCode(String retCode) {
this.retCode = retCode;
}
public String getRetMsg() {
return retMsg;
}
public void setRetMsg(String retMsg) {
this.retMsg = retMsg;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getUse() {
return use;
}
public void setUse(String use) {
this.use = use;
}
public String getBtchNb() {
return btchNb;
}
public void setBtchNb(String btchNb) {
this.btchNb = btchNb;
}
}
解析caps.202.001.01.xml报文
前面的准备工作完成,我们来解析一个xml报文,试一试
测试代码
package com.xml.parse;
import java.io.IOException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.xml.parse.entity.Document;
import com.xml.parse.entity.caps202.Caps202Body;
import com.xml.parse.entity.caps202.Caps202Head;
import com.xml.parse.utils.XStreamXmlUtil;
import com.xml.utils.TxtFileReader;
public class Test {
public static void main(String[] args) throws IOException {
//从文件中读取xml字符串
TxtFileReader tfr = new TxtFileReader("./file/caps.202.001.01.xml");
String text = tfr.readAll();
//System.out.println("xml text: \n" + text);
//解析caps.202.001.01.xml,传入是对应的Caps202Head,和 Caps202Body
//一行代码就搞定了
Document doc = XStreamXmlUtil.xmlToBean(text, Document.class, Caps202Head.class, Caps202Body.class);
//使用json格式展示解析后的结果,方便观察不是必须的
System.out.println(JSON.toJSONString(doc,SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteDateUseDateFormat));
}
}
解析结果:
再解析一个新报文,看看实际效果
样例caps.204.001.01.xml
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:cbcc:std:caps:2020:tech:xsd:caps.202.001.01" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Message>
<Head>
<CorpNo>3310000001</CorpNo>
<ResFlag>SUCC</ResFlag>
<ErrorCode>I000</ErrorCode>
<ErrorMsg>平台检验成功</ErrorMsg>
</Head>
<Body>
<ReturnTime>2020-10-30T09:26:51</ReturnTime>
<SysSeqNo>2020103050006167</SysSeqNo>
<SerialNum>2020103000000001</SerialNum>
<RetCode>111111</RetCode>
<RetMsg>业务处理中</RetMsg>
<Remark>专项科研经费</Remark>
<Use>服务费202010</Use>
<BtchNb>50006167</BtchNb>
</Body>
</Message>
</Document>
建对应实体类Caps204Body和Caps204Head,仿照Caps202建就可以了
package com.xml.parse.entity.caps204;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.xml.parse.entity.BaseHead;
@XStreamAlias("Head")
public class Caps204Head extends BaseHead{
@XStreamAlias("CorpNo")
private String corpNo;
@XStreamAlias("ResFlag")
private String resFlag;
@XStreamAlias("ErrorCode")
private String errorCode;
@XStreamAlias("ErrorMsg")
private String errorMsg;
public String getCorpNo() {
return corpNo;
}
public void setCorpNo(String corpNo) {
this.corpNo = corpNo;
}
public String getResFlag() {
return resFlag;
}
public void setResFlag(String resFlag) {
this.resFlag = resFlag;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
package com.xml.parse.entity.caps204;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.xml.parse.entity.BaseBody;
@XStreamAlias("Body")
public class Caps204Body extends BaseBody{
@XStreamAlias("ReturnTime")
private String returnTime;
@XStreamAlias("SysSeqNo")
private String sysSeqNo;
@XStreamAlias("SerialNum")
private String serialNum;
@XStreamAlias("RetCode")
private String retCode;
@XStreamAlias("RetMsg")
private String retMsg;
@XStreamAlias("Remark")
private String remark;
@XStreamAlias("Use")
private String use;
@XStreamAlias("BtchNb")
private String btchNb;
public String getReturnTime() {
return returnTime;
}
public void setReturnTime(String returnTime) {
this.returnTime = returnTime;
}
public String getSysSeqNo() {
return sysSeqNo;
}
public void setSysSeqNo(String sysSeqNo) {
this.sysSeqNo = sysSeqNo;
}
public String getSerialNum() {
return serialNum;
}
public void setSerialNum(String serialNum) {
this.serialNum = serialNum;
}
public String getRetCode() {
return retCode;
}
public void setRetCode(String retCode) {
this.retCode = retCode;
}
public String getRetMsg() {
return retMsg;
}
public void setRetMsg(String retMsg) {
this.retMsg = retMsg;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getUse() {
return use;
}
public void setUse(String use) {
this.use = use;
}
public String getBtchNb() {
return btchNb;
}
public void setBtchNb(String btchNb) {
this.btchNb = btchNb;
}
}
测试解析代码
package com.xml.parse;
import java.io.IOException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.xml.parse.entity.Document;
import com.xml.parse.entity.caps204.Caps204Body;
import com.xml.parse.entity.caps204.Caps204Head;
import com.xml.parse.utils.XStreamXmlUtil;
import com.xml.utils.TxtFileReader;
public class Test {
public static void main(String[] args) throws IOException {
//从文件中读取xml字符串
TxtFileReader tfr = new TxtFileReader("./file/caps.202.001.01.xml");
String text = tfr.readAll();
//System.out.println("xml text: \n" + text);
// //解析xml
// Document doc = XStreamXmlUtil.xmlToBean(text, Document.class, Caps202Head.class, Caps202Body.class);
//这里传Caps204Head和Caps204Body 即可
Document doc = XStreamXmlUtil.xmlToBean(text, Document.class, Caps204Head.class, Caps204Body.class);
//使用json格式,展示解析后的结构
System.out.println(JSON.toJSONString(doc,SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteDateUseDateFormat));
}
}
解析结果
总结一下
当我们把准备工作做完,解析新的xml就很简单了。针对不同的Head,Body创建不同子类。在解析时传入相应的业务子类,就可以完成解析非常方便。要注意一点,实际使用时,业务子类要强制转换一下,才可以获取到业务数据。
补充读文件的类
package com.xml.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/**
* 读文本文件
* @author jiyh
* @create 2012-03-21 13:45
*/
public class TxtFileReader {
private File file;
private FileInputStream fis;
private InputStreamReader isr;
private BufferedReader br;
public TxtFileReader(){
}
/**
* 使用从文件信息中获得字符集, 构造读文件对象
* @param fullName
* @throws IOException
*/
public TxtFileReader(String fullName) throws IOException{
this(new File(fullName));
}
/**
* 使用指定字符集, 构造读文件对象
* @param fullName
* @param charset
* @throws IOException
*/
public TxtFileReader(String fullName, String charset) throws IOException{
this( new File(fullName), charset);
}
/**
* 使用从文件信息中获得字符集, 构造读文件对象
* @param file
* @throws IOException
*/
public TxtFileReader(File file) throws IOException{
this.file = file;
this.fis = new FileInputStream(file);
this.isr = new InputStreamReader(fis);
this.br = new BufferedReader(isr);
}
/**
* 使用指定字符集, 构造读文件对象
* @param fullName
* @param charset
* @throws IOException
*/
public TxtFileReader(File file, String charset) throws IOException{
this.file = file;
this.fis = new FileInputStream(file);
if(charset == null || charset.equals("")){
this.isr = new InputStreamReader(fis);
}else {
this.isr = new InputStreamReader(fis, charset);
}
// this.isr = new InputStreamReader(fis, charset);
this.br = new BufferedReader(isr);
}
/**
* 读一行数据
* @return
* @throws IOException
*/
public String readLine() throws IOException{
return this.br.readLine();
}
/**
* 按行读取数据,并把数据保存到集合中
* 以"#"开头的行跳过
* @return
* @throws IOException
*/
public List<String> readLineList() throws IOException{
List<String> lineList = new ArrayList<String>();
String line = null;
while ((line = this.readLine() ) != null) {
line = line.trim();
if (line.startsWith("#")) {
continue;
}
lineList.add(line);
}
this.close();
return lineList;
}
/**
* 读取说有文本
* 以"#"开头的行跳过
* @return
* @throws IOException
*/
public String readAll() throws IOException{
List<String> lineList = new ArrayList<String>();
StringBuffer text = new StringBuffer();
String line = null;
String br =System.getProperty( "line.separator");
while ((line = this.readLine() ) != null) {
// line = line.trim();
if (line.contains("#")) {
continue;
}
// lineList.add(line);
text.append(line).append(br);
}
this.close();
return text.toString();
}
/**
* 返回当前读文件的流对象
* @return
*/
public BufferedReader getBr() {
return br;
}
/**
* 关闭文件流
* @throws IOException
*/
public void close(){
if (this.br != null) {
try {
this.br.close();
} catch (IOException e) {
}
}
if (this.isr != null) {
try {
this.isr.close();
} catch (IOException e) {
}
}
if (this.fis != null) {
try {
this.fis.close();
} catch (IOException e) {
}
}
}
/**
* 删除当前文件
*/
public void delete(){
if(this.file.exists() && this.file.isFile()){
this.file.delete();
}
}
}