该文章只用于客户端如何通过xml格式请求服务端以及将服务端返回的xml格式的报文映射成实体类
服务端请绕道
1.前景
写这篇文章的目的主要是接到一个需求,需要调用长春的核心系统,一直以为是通过json传递数据的,直到测试时才发现核心系统是通过xml来当做报文
2. 开始
2.1 Utils准备
在进行组装xml报文前,我们需要工具类的帮助
这里推荐我们公司内部的XmlUtil,当然网上的也可以
public class XmlUtil {
/**
* object => xml
* @param obj
* @return
*/
public static String Object2Xml(Object obj) {
XStream xs = new XStream();
xs.processAnnotations(obj.getClass());
setAnnotations(xs, obj.getClass());
String xmlStr = xs.toXML(obj);
String xmlHead = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
return xmlHead + xmlStr;
}
/**
* xml=>object
* @param xml
* @param cls
* @return
*/
public static <T> T Xml2Object(String xml, Class<T> cls) {
XStream xs = new XStream();
xs.processAnnotations(cls);
setAnnotations(xs, cls);
T object = (T) xs.fromXML(xml);
return object;
}
/**
* 注解设置
* @param xs
* @param cls
*/
private static <T> void setAnnotations(XStream xs, Class<T> cls) {
Field f[] = cls.getDeclaredFields();
for (int i = 0; i < f.length; i++) {
Field field = f[i];
xs.processAnnotations(field.getType());
}
}
public static <T> T toBean(String xml, Class<T> cls) {
XStream xstream = new XStream(new DomDriver("UTF-8"));
xstream.processAnnotations(cls);
xstream.setClassLoader(cls.getClassLoader());
return (T) xstream.fromXML(xml);
}
/**
* Bean => Xml
* @param obj
* @return
*/
public static String toXml(Object obj,String dtoName) {
XStream xstream=new XStream(new DomDriver("UTF-8"));
xstream.autodetectAnnotations(true);
xstream.alias(dtoName, obj.getClass());
String xml=xstream.toXML(obj);
return xml;
}
public static String toSoapXml(Object obj,String functionName,String xmlnsirx) {
String start = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:irx=\""+xmlnsirx+"/\">\n" +
" <soapenv:Header/>\n" +
" <soapenv:Body>\n"+
"<irx:"+functionName+">\n";
String end = "</irx:"+functionName+">\n" +
" </soapenv:Body>\n" +
"</soapenv:Envelope>\n";
XStream xstream=new XStream(new DomDriver("UTF-8"));
xstream.autodetectAnnotations(true);
xstream.alias("requestDTO", obj.getClass());
String xml=xstream.toXML(obj);
return start+xml+end;
}
}
第二个Utils Httputils (网上随便找一个就行,这里推荐我们公司使用的)
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.1.14</version>
</dependency>
导入这个依赖,其中的HttpUtil类就是我们需要的工具类
2.2 请求报文组装
这里请求报文的组装需要用到XmlUtil.toSoapXml(Object obj,String functionName,String xmlnsirx)
这个方法,这里简单解释一下这三个参数的含义。第一个参数当然就是我们的参数,通常是pojo或者map都行,第二个参数可以理解成接口名,后面有示例。第三个参数是XML命名空间。其中第二个跟第三个参数我们都不需要关心,通常是服务端提供的。
示例:
如服务端提供给我们的地址是http://192.168.1.1:9080/icfWeb/ws/TruckTermSearchListWrapper?wsdl
那么,我们的方法这样写:
String fXMLString = XmlUtil.toSoapXml(reqDTO, "TruckTermSearchList","http://irxcx010.afterloan.credit.faf.com");
看到这,这里有个人会问第三个参数http://irxcx010.afterloan.credit.faf.com是哪里来的,这里就需要安装另外一个工具啦。
2.3 SOAPUI介绍
根据上文所提到的,SOAPUI就是我们要提供的工具。这个工具可以理解成发送xml报文的类似postman的工具
2.3.1 下载
2.3.2 使用
打开页面点击新建
输入地址(地址找你们服务端提供,不要再问我了)
ProjectName随意取,下面那个WSDL就是你请求的地址
点击ok,看到以下截图,就是我们的第三个参数http://irxcx010.afterloan.credit.faf.com的由来
2.3.3 工具小结
这里建议在写java代码前先使用该工具调试一下请求的接口有没有问题,以及接口返回的报文格式是怎么样的,有利于我们后面java代码的编写
2.4 发送请求
直接上代码,简单一行
String res = HttpUtil.post(url, fXMLString);
url就是请求的地址
2.5 返回报文接收
2.5.1 报文截取
我们可以看到返回的报文乱七八糟,而我们真正需要的只是框框里的这几行,我们可以通过StringUtils将返回的报文进行截取。没有该工具类的用String的substring方法也许,是一样的。
这行代码的目的就是截取返回的报文。
res = StringUtils.substringBetween(res, "faf.com/\">", "</ns2:");
截取完,变这样了
<return><applySource>APP</applySource><comtype>3</comtype><ptype>3</ptype><respCode>0000</respCode><respMsg>成功!</respMsg><entityList><id>16190</id><applySource>xd</applySource><applydate>2020-04-26</applydate><bargainNo>JHBHBJC02454</bargainNo><loanmoney>100000.0</loanmoney><nstatus>5</nstatus><payDate>20</payDate><plateNum>895</plateNum><reasonType>15056</reasonType><reasonTypeName>张大哥大哥</reasonTypeName><term>1</term><termType>3</termType><timeLimit>25</timeLimit></entityList></return>
2.5.2 xml与实体类转换
报文返回的形式是String类型,但通常我们都需要将它转化为实体类,下面就介绍如何将xml报文与我们实体类映射
首先,添加依赖
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.9</version>
<scope>compile</scope>
</dependency>
在实体类里添加注解
@XStreamAlias("return")
public class TruckTermExtendSearchWrapperResDTO {
@XStreamAlias("comtype")
private String comtype;
@XStreamAlias("ptype")
private String ptype;
@XStreamAlias("respCode")
private String respCode;
@XStreamAlias("respMsg")
private String respMsg;
@XStreamAlias("applySource")
private String applySource;
@XStreamImplicit(itemFieldName="entityList")
private List<EntityDTO> entityList;
public String getComtype() {
return comtype;
}
public void setComtype(String comtype) {
this.comtype = comtype;
}
public String getPtype() {
return ptype;
}
public void setPtype(String ptype) {
this.ptype = ptype;
}
public String getRespCode() {
return respCode;
}
public void setRespCode(String respCode) {
this.respCode = respCode;
}
public String getRespMsg() {
return respMsg;
}
public void setRespMsg(String respMsg) {
this.respMsg = respMsg;
}
public String getApplySource() {
return applySource;
}
public void setApplySource(String applySource) {
this.applySource = applySource;
}
public List<EntityDTO> getEntityList() {
return entityList;
}
public void setEntityList(List<EntityDTO> entityList) {
this.entityList = entityList;
}
}
这里需要特别注意的是,如果返回的xml格式有list的话,需要使用@XStreamImplicit(itemFieldName="entityList")注解
注解值只需要跟xml标签映射就行了,简单实用
2.5.3 映射方法
上面讲了实体类添加的注解,注解添加完之后需要通过一个方法进行映射
TruckTermExtendSearchWrapperResDTO resDTO = XmlUtil.toBean(res, TruckTermExtendSearchWrapperResDTO.class);
映射完毕后,看看你的实体类属性是否都映射上了,后面就可以进行自己的业务逻辑了