上一节使用SOAPMessage调用WebService接口,但是比较麻烦的是参数的拼装需要一个一个add,比较麻烦。此次介绍使用JAXBContent转化Bean与Xml,然后利用http请求(如Okhttp)发送Xml组装的报文。
本文demo代码:JAXBContent转换Bean与Xml
目标
先看一下请求与结果:
再看一下本demo最终的类结构
- bean目录分为request和response,代表的分别是请求和相应。
- 由于Xml有多层结点,造成Bean也会有多层嵌套。
- NameSpace和package-info.java定义了命名空间,用于生成Xml和解析Xml。
- HttpRequest使用Okhttp发送请求。
- XmlUtil则封装了Bean转化为Xml、Xml转化为Bean两个方法。
- Test类进行测试。
1.Bean To Xml
1.1 转化方法
Bean To Xml转化方法是XmlUtil中的一个方法。具体方法如下:
/**
* transfer bean to xml
*
* @param obj 待转化类对象
* @param clazz 待转化类
* @return 转化后的xml字符串
* @throws JAXBException
*/
public static String beanToXml(Object obj, java.lang.Class<?> clazz) throws JAXBException {
String result = null;
try {
JAXBContext context = JAXBContext.newInstance(clazz);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
StringWriter writer = new StringWriter();
marshaller.marshal(obj, writer);
result = writer.toString();
} catch (JAXBException e) {
e.printStackTrace();
System.out.println("JAXBException happened.");
}
return result;
}
/**
* transfer bean to xml
*
* @param obj 待转化类对象
* @param clazz 待转化类
* @return 转化后的xml字符串
* @throws JAXBException
*/
public static String beanToXml(Object obj, java.lang.Class<?> clazz) throws JAXBException {
String result = null;
try {
JAXBContext context = JAXBContext.newInstance(clazz);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
StringWriter writer = new StringWriter();
marshaller.marshal(obj, writer);
result = writer.toString();
} catch (JAXBException e) {
e.printStackTrace();
System.out.println("JAXBException happened.");
}
return result;
}
有了转化方法,不代表转化就可以成功了。因为Xml是有一定格式的,转化的格式如何规定呢?这里可以使用@Xml类注解进行规定。
1.2 @XML类注解
@XmlRootElement 代表xml根节点
@XmlType 可以定义xml节点中元素的顺序
@XmlElement 表名是当前节点下的一个子节点
@XmlAttribute 表名是当前节点的属性
有的注解中可以定义name属性,指定其在Xml中的名称;namespace属性,指定其属于某一个命名空间。
1.3 定义命名空间
NameSpace中的内容如下
package cn.hewie.webService.bean.request;
public class NameSpace {
public static final String BASIC_PREFIX = "soap";
public static final String BASIC_URI = "http://www.w3.org/2003/05/soap-envelope";
public static final String TEMP_URI = "http://tempuri.org/";
public static final String TEMP_PREFIX = "tem";
}
package cn.hewie.webService.bean.request;
public class NameSpace {
public static final String BASIC_PREFIX = "soap";
public static final String BASIC_URI = "http://www.w3.org/2003/05/soap-envelope";
public static final String TEMP_URI = "http://tempuri.org/";
public static final String TEMP_PREFIX = "tem";
}
package-info.java中的内容如下
@XmlSchema(
xmlns = {
@XmlNs(prefix = NameSpace.BASIC_PREFIX, namespaceURI = NameSpace.BASIC_URI),
@XmlNs(prefix = NameSpace.TEMP_PREFIX, namespaceURI = NameSpace.TEMP_URI)
})
package cn.hewie.webService.bean.request;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlSchema;
@XmlSchema(
xmlns = {
@XmlNs(prefix = NameSpace.BASIC_PREFIX, namespaceURI = NameSpace.BASIC_URI),
@XmlNs(prefix = NameSpace.TEMP_PREFIX, namespaceURI = NameSpace.TEMP_URI)
})
package cn.hewie.webService.bean.request;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlSchema;
1.4 使用注解标识Bean
有了上述注解和命名空间基础后,就可以使用注解标识Bean了。例如上述请求中,最外层结点定位为RequestEntity对象,可以定义如下:
import cn.hewie.webService.bean.response.NameSpace;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlRootElement(name = "Envelope", namespace = NameSpace.BASIC_URI)
@XmlType(propOrder = {"header", "body"})
public class RequestEntity {
protected RequestHeader header;
protected RequestBody body;
@XmlElement(name = "Header", namespace = NameSpace.BASIC_URI)
public RequestHeader getHeader() {
return header;
}
public void setHeader(RequestHeader header) {
this.header = header;
}
@XmlElement(name = "Body", namespace = NameSpace.BASIC_URI)
public RequestBody getBody() {
return body;
}
public void setBody(RequestBody body) {
this.body = body;
}
}
import cn.hewie.webService.bean.response.NameSpace;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlRootElement(name = "Envelope", namespace = NameSpace.BASIC_URI)
@XmlType(propOrder = {"header", "body"})
public class RequestEntity {
protected RequestHeader header;
protected RequestBody body;
@XmlElement(name = "Header", namespace = NameSpace.BASIC_URI)
public RequestHeader getHeader() {
return header;
}
public void setHeader(RequestHeader header) {
this.header = header;
}
@XmlElement(name = "Body", namespace = NameSpace.BASIC_URI)
public RequestBody getBody() {
return body;
}
public void setBody(RequestBody body) {
this.body = body;
}
}
这样就标识了最外层的Envelope,还指定了其含有子节点header和body,并分别指定了命名空间。
由于header结点为空,我们可以直接创建一个对象即可。
package cn.hewie.webService.bean.request;
public class RequestHeader {
}
package cn.hewie.webService.bean.request;
public class RequestHeader {
}
主要还是body节点,其中又包含了一个对象。
package cn.hewie.webService.bean.request;
import javax.xml.bind.annotation.XmlElement;
public class RequestBody {
private IsWaybillExistsRequest isWaybillExistsRequest;
@XmlElement(name = "IsWaybillExists", namespace = NameSpace.TEMP_URI)
public IsWaybillExistsRequest getIsWaybillExistsRequest() {
return isWaybillExistsRequest;
}
public void setIsWaybillExistsRequest(IsWaybillExistsRequest isWaybillExistsRequest) {
this.isWaybillExistsRequest = isWaybillExistsRequest;
}
}
package cn.hewie.webService.bean.request;
import javax.xml.bind.annotation.XmlElement;
public class RequestBody {
private IsWaybillExistsRequest isWaybillExistsRequest;
@XmlElement(name = "IsWaybillExists", namespace = NameSpace.TEMP_URI)
public IsWaybillExistsRequest getIsWaybillExistsRequest() {
return isWaybillExistsRequest;
}
public void setIsWaybillExistsRequest(IsWaybillExistsRequest isWaybillExistsRequest) {
this.isWaybillExistsRequest = isWaybillExistsRequest;
}
}
依次类推
IsWaybillExistsRequest类如下
package cn.hewie.webService.bean.request;
import javax.xml.bind.annotation.*;
@XmlType(propOrder = {"clientInfo", "waybillNo"})
public class IsWaybillExistsRequest {
private String waybillNo;
private ClientInfo clientInfo;
@XmlElement(name = "WaybillNo", namespace = NameSpace.TEMP_URI)
public String getWaybillNo() {
return waybillNo;
}
public void setWaybillNo(String waybillNo) {
this.waybillNo = waybillNo;
}
@XmlElement(name = "ClientInfo", namespace = NameSpace.TEMP_URI)
public ClientInfo getClientInfo() {
return clientInfo;
}
public void setClientInfo(ClientInfo clientInfo) {
this.clientInfo = clientInfo;
}
}
package cn.hewie.webService.bean.request;
import javax.xml.bind.annotation.*;
@XmlType(propOrder = {"clientInfo", "waybillNo"})
public class IsWaybillExistsRequest {
private String waybillNo;
private ClientInfo clientInfo;
@XmlElement(name = "WaybillNo", namespace = NameSpace.TEMP_URI)
public String getWaybillNo() {
return waybillNo;
}
public void setWaybillNo(String waybillNo) {
this.waybillNo = waybillNo;
}
@XmlElement(name = "ClientInfo", namespace = NameSpace.TEMP_URI)
public ClientInfo getClientInfo() {
return clientInfo;
}
public void setClientInfo(ClientInfo clientInfo) {
this.clientInfo = clientInfo;
}
}
ClientInfo.java如下
package cn.hewie.webService.bean.request;
import javax.xml.bind.annotation.XmlElement;
public class ClientInfo {
private ClientAddress clientAddress;
private String clientID;
private String password;
private String version;
@XmlElement(name = "ClientAddress", namespace = NameSpace.TEMP_URI)
public ClientAddress getClientAddress() {
return clientAddress;
}
public void setClientAddress(ClientAddress clientAddress) {
this.clientAddress = clientAddress;
}
@XmlElement(name = "ClientID", namespace = NameSpace.TEMP_URI)
public String getClientID() {
return clientID;
}
public void setClientID(String clientID) {
this.clientID = clientID;
}
@XmlElement(name = "Password", namespace = NameSpace.TEMP_URI)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@XmlElement(name = "Version", namespace = NameSpace.TEMP_URI)
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
package cn.hewie.webService.bean.request;
import javax.xml.bind.annotation.XmlElement;
public class ClientInfo {
private ClientAddress clientAddress;
private String clientID;
private String password;
private String version;
@XmlElement(name = "ClientAddress", namespace = NameSpace.TEMP_URI)
public ClientAddress getClientAddress() {
return clientAddress;
}
public void setClientAddress(ClientAddress clientAddress) {
this.clientAddress = clientAddress;
}
@XmlElement(name = "ClientID", namespace = NameSpace.TEMP_URI)
public String getClientID() {
return clientID;
}
public void setClientID(String clientID) {
this.clientID = clientID;
}
@XmlElement(name = "Password", namespace = NameSpace.TEMP_URI)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@XmlElement(name = "Version", namespace = NameSpace.TEMP_URI)
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
经过测试,该接口只需要节点IsWaybillExists下存在
<tem:ClientID></tem:ClientID>
就可以正确返回了(其他节点都有optional标示),为了简单起见,更下层的其他类我们就不列举出实现了(事实上demo中没有实现ClientContact类)。
2. Xml To Bean
将Xml转化为Bean与上述过程类似。
2.1 转化方法
XmlUtil中转化方法如下:
/**
* transfer xml to bean
*
* @param xml 待转化的xml字符串
* @param c 转化后的类
* @param <T> 转化后类类型
* @return 转化后的类对象
*/
@SuppressWarnings("unchecked")
public static <T> T xmlToBean(String xml, Class<T> c) {
T t = null;
try {
JAXBContext context = JAXBContext.newInstance(c);
Unmarshaller unmarshaller = context.createUnmarshaller();
t = (T) unmarshaller.unmarshal(new StringReader(xml));
} catch (JAXBException e) {
e.printStackTrace();
}
return t;
}
/**
* transfer xml to bean
*
* @param xml 待转化的xml字符串
* @param c 转化后的类
* @param <T> 转化后类类型
* @return 转化后的类对象
*/
@SuppressWarnings("unchecked")
public static <T> T xmlToBean(String xml, Class<T> c) {
T t = null;
try {
JAXBContext context = JAXBContext.newInstance(c);
Unmarshaller unmarshaller = context.createUnmarshaller();
t = (T) unmarshaller.unmarshal(new StringReader(xml));
} catch (JAXBException e) {
e.printStackTrace();
}
return t;
}
2.2 命名空间
与上述类似,但注意不要把命名空间与属性弄错。
NameSpace.java
package cn.hewie.webService.bean.response;
public class NameSpace {
public static final String BASIC_PREFIX = "soap";
public static final String BASIC_URI = "http://www.w3.org/2003/05/soap-envelope";
public static final String TEMP_PREFIX = "";
public static final String TEMP_URI = "http://tempuri.org/";
public static final String XSI_PREFIX = "xsi";
public static final String XSI_URI = "http://www.w3.org/2001/XMLSchema-instance";
public static final String XSD_PREFIX = "xsd";
public static final String XSD_URI = "http://www.w3.org/2001/XMLSchema";
}
package cn.hewie.webService.bean.response;
public class NameSpace {
public static final String BASIC_PREFIX = "soap";
public static final String BASIC_URI = "http://www.w3.org/2003/05/soap-envelope";
public static final String TEMP_PREFIX = "";
public static final String TEMP_URI = "http://tempuri.org/";
public static final String XSI_PREFIX = "xsi";
public static final String XSI_URI = "http://www.w3.org/2001/XMLSchema-instance";
public static final String XSD_PREFIX = "xsd";
public static final String XSD_URI = "http://www.w3.org/2001/XMLSchema";
}
package-info.java
@XmlSchema(
xmlns = {
@XmlNs(prefix = NameSpace.BASIC_PREFIX, namespaceURI = NameSpace.BASIC_URI),
@XmlNs(prefix = NameSpace.XSI_PREFIX, namespaceURI = NameSpace.XSI_URI),
@XmlNs(prefix = NameSpace.XSD_PREFIX, namespaceURI = NameSpace.XSD_URI)
})
package cn.hewie.webService.bean.response;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlSchema;
@XmlSchema(
xmlns = {
@XmlNs(prefix = NameSpace.BASIC_PREFIX, namespaceURI = NameSpace.BASIC_URI),
@XmlNs(prefix = NameSpace.XSI_PREFIX, namespaceURI = NameSpace.XSI_URI),
@XmlNs(prefix = NameSpace.XSD_PREFIX, namespaceURI = NameSpace.XSD_URI)
})
package cn.hewie.webService.bean.response;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlSchema;
2.3 使用注解标识Bean
与上述类似,这里不再赘述。
3. 发送请求
利用Okhttp发送请求类HttpRequest如下:
package cn.hewie.webService.client;
import okhttp3.*;
import java.io.IOException;
public class HttpRequest {
public static String execute(String postBody) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.post(RequestBody.create(MediaType.parse("application/soap+xml; charset=utf-8"), postBody))
.url("https://infotrack.naqelexpress.com/NaqelAPIServices/NaqelAPIDemo/5.0/XMLShippingService.asmx")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
System.out.println("服务器错误:" + response);
}
// Headers responseHeaders = response.headers();
// for (int i = 0; i < responseHeaders.size(); i++) {
// System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
// }
String result = response.body().string();
return result;
}
}
package cn.hewie.webService.client;
import okhttp3.*;
import java.io.IOException;
public class HttpRequest {
public static String execute(String postBody) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.post(RequestBody.create(MediaType.parse("application/soap+xml; charset=utf-8"), postBody))
.url("https://infotrack.naqelexpress.com/NaqelAPIServices/NaqelAPIDemo/5.0/XMLShippingService.asmx")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
System.out.println("服务器错误:" + response);
}
// Headers responseHeaders = response.headers();
// for (int i = 0; i < responseHeaders.size(); i++) {
// System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
// }
String result = response.body().string();
return result;
}
}
1.6 测试
测试的时候只需要正确组装类的层级结构,然后调用相应接口,就可以正确处理了。
获得的结果进行转化后,即可获得正确的类结构。
public static void testHttpRequest() {
ClientAddress clientAddress = new ClientAddress();
clientAddress.setPhoneNumber("11111111111");
clientAddress.setPOBox("aaa");
clientAddress.setZipCode("111111");
clientAddress.setFax("aaa");
clientAddress.setFirstAddress("111");
clientAddress.setLocation("111");
clientAddress.setCountryCode("10200");
clientAddress.setCityCode("22");
ClientInfo clientInfo = new ClientInfo();
clientInfo.setClientAddress(clientAddress);
clientInfo.setClientID("1");
clientInfo.setPassword("111");
clientInfo.setVersion("1.0");
IsWaybillExistsRequest isWaybillExistsRequest = new IsWaybillExistsRequest();
isWaybillExistsRequest.setClientInfo(clientInfo);
isWaybillExistsRequest.setWaybillNo("111");
RequestBody requestBody = new RequestBody();
requestBody.setIsWaybillExistsRequest(isWaybillExistsRequest);
RequestHeader requestHeader = new RequestHeader();
RequestEntity requestEntity = new RequestEntity();
requestEntity.setHeader(requestHeader);
requestEntity.setBody(requestBody);
try {
String body = XmlUtil.beanToXml(requestEntity, RequestEntity.class);
System.out.println("==============Bean to Xml==============");
System.out.println(body);
String response = HttpRequest.execute(body);
System.out.println("=============response body==============");
System.out.println(response);
ResponseEntity responseEntity = XmlUtil.xmlToBean(response, ResponseEntity.class);
System.out.println("=============Xml to Bean================");
System.out.println(responseEntity.getResponseBody().getIsWaybillExistsResponse().getWaybillExistsResult());
} catch (JAXBException e) {
e.printStackTrace();
System.out.println("JAXBException happened.");
} catch (Exception e) {
e.printStackTrace();
System.out.println("unexpected error happened.");
}
}
public static void testHttpRequest() {
ClientAddress clientAddress = new ClientAddress();
clientAddress.setPhoneNumber("11111111111");
clientAddress.setPOBox("aaa");
clientAddress.setZipCode("111111");
clientAddress.setFax("aaa");
clientAddress.setFirstAddress("111");
clientAddress.setLocation("111");
clientAddress.setCountryCode("10200");
clientAddress.setCityCode("22");
ClientInfo clientInfo = new ClientInfo();
clientInfo.setClientAddress(clientAddress);
clientInfo.setClientID("1");
clientInfo.setPassword("111");
clientInfo.setVersion("1.0");
IsWaybillExistsRequest isWaybillExistsRequest = new IsWaybillExistsRequest();
isWaybillExistsRequest.setClientInfo(clientInfo);
isWaybillExistsRequest.setWaybillNo("111");
RequestBody requestBody = new RequestBody();
requestBody.setIsWaybillExistsRequest(isWaybillExistsRequest);
RequestHeader requestHeader = new RequestHeader();
RequestEntity requestEntity = new RequestEntity();
requestEntity.setHeader(requestHeader);
requestEntity.setBody(requestBody);
try {
String body = XmlUtil.beanToXml(requestEntity, RequestEntity.class);
System.out.println("==============Bean to Xml==============");
System.out.println(body);
String response = HttpRequest.execute(body);
System.out.println("=============response body==============");
System.out.println(response);
ResponseEntity responseEntity = XmlUtil.xmlToBean(response, ResponseEntity.class);
System.out.println("=============Xml to Bean================");
System.out.println(responseEntity.getResponseBody().getIsWaybillExistsResponse().getWaybillExistsResult());
} catch (JAXBException e) {
e.printStackTrace();
System.out.println("JAXBException happened.");
} catch (Exception e) {
e.printStackTrace();
System.out.println("unexpected error happened.");
}
}
运行后,测试结果如下:
==============Bean to Xml==============
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<soap:Envelope xmlns:tem="http://tempuri.org/" xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header/>
<soap:Body>
<tem:IsWaybillExists>
<tem:ClientInfo>
<tem:ClientAddress>
<tem:PhoneNumber>11111111111</tem:PhoneNumber>
<tem:POBox>aaa</tem:POBox>
<tem:ZipCode>111111</tem:ZipCode>
<tem:Fax>aaa</tem:Fax>
<tem:FirstAddress>111</tem:FirstAddress>
<tem:Location>111</tem:Location>
<tem:CountryCode>10200</tem:CountryCode>
<tem:CityCode>22</tem:CityCode>
</tem:ClientAddress>
<tem:ClientID>1</tem:ClientID>
<tem:Password>111</tem:Password>
<tem:Version>1.0</tem:Version>
</tem:ClientInfo>
<tem:WaybillNo>111</tem:WaybillNo>
</tem:IsWaybillExists>
</soap:Body>
</soap:Envelope>
=============response body==============
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><IsWaybillExistsResponse xmlns="http://tempuri.org/"><IsWaybillExistsResult>true</IsWaybillExistsResult></IsWaybillExistsResponse></soap:Body></soap:Envelope>
=============Xml to Bean================
true