WebService如何封裝XML請求 以及解析接口返回的XML
1、封裝XML報文對象
博主在調第三方接口時,經常需要封裝XML去請求第三方的數據,在Web開發時,需要經常用到,因此也打算寫篇文章記錄下本人在思考和尋求答案的過程。
1-1 XML的一些基本常識
一般在參考一些API的文檔時,JAVA開發一般是根據特定的API要求去對數據進行封裝,在此,我將采用舉例的方式來說明,已經應用場景。在封裝XML對象時,首先我們得了解封裝XML對象試用方式,一般采取Class類注解的形式去實現。如@XmlType、@XmlAccessorType、@XmlRootElement、 @XmlElement等。
@XmlType(propOrder ={ "Header", "MessageType", "Message" }) // 指定序列成的xml節點順序
@XmlAccessorType(value = XmlAccessType.FIELD) // 訪問類型改為字段
@XmlRootElement(name = "AmazonEnvelope")//封裝XML對象的根節點
1-2 封裝XML針對某些特定API請求參數。這里以對接亞馬遜的某些接口舉例
以下為我舉例加入某接口需要對參數封裝XML:
-
/*
-
*
<?xml version="1.0" encoding="UTF-8"?>
-
*
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
-
*
<Header>
-
*
<DocumentVersion>1.02
</DocumentVersion>
-
*
<MerchantIdentifier>A23G8Q8ZIKBK8C
</MerchantIdentifier>
-
*
</Header>
-
*
<MessageType>ProcessingReport
</MessageType>
-
*
<Message>
-
*
<MessageID>1
</MessageID>
-
*
<ProcessingReport>
-
*
<DocumentTransactionID>57320017876
</DocumentTransactionID>
-
*
<StatusCode>Complete
</StatusCode>
-
*
<ProcessingSummary>
-
*
<MessagesProcessed>15
</MessagesProcessed>
-
*
<MessagesSuccessful>13
</MessagesSuccessful>
-
*
<MessagesWithError>2
</MessagesWithError>
-
*
<MessagesWithWarning>0
</MessagesWithWarning>
-
*
</ProcessingSummary>
-
*
<Result>
-
*
<MessageID>3
</MessageID>
-
*
<ResultCode>Error
</ResultCode>
-
*
<ResultMessageCode>25
</ResultMessageCode>
-
*
<ResultDescription>We are unable to process the XML feed because one or more items are invalid. Please re-submit the feed.
</ResultDescription>
-
*
</Result>
-
*
<Result>
-
*
<MessageID>4
</MessageID>
-
*
<ResultCode>Error
</ResultCode>
-
*
<ResultMessageCode>25
</ResultMessageCode>
-
*
<ResultDescription>We are unable to process the XML feed because one or more items are invalid. Please re-submit the feed.
</ResultDescription>
-
*
</Result>
-
*
</ProcessingReport>
-
*
</Message>
-
*
</AmazonEnvelope>
-
-
*/
如果看到這種XML格式,去封裝請求對象如何封裝呢?
我們如果有了解過XML這種語言就知道,XML可以理解為一顆樹,有父子根節點構成。其實Spring 內部去解析XML時,也是根據這種特性去解析的。因為我們最原始MVC 需要大量的配置XML 注入bean。以及配置事物等等。我們通過分析可以發現,外部根節點為AmazonEnvelope,子節點Header、MessageType、Message,然后Message節點下又有子節點MessageID、ProcessingReport。依次類推,可以構造AmazonEnvelope大對象,然后以此為根節點建造子節點對象,這里舉例兩個如下:
-
package com.aukey.supply.chain.domain.test;
-
-
import javax.xml.bind.
annotation.XmlAccessType;
-
import javax.xml.bind.
annotation.XmlAccessorType;
-
import javax.xml.bind.
annotation.XmlElement;
-
import javax.xml.bind.
annotation.XmlRootElement;
-
import javax.xml.bind.
annotation.XmlType;
-
-
@XmlType(propOrder =
-
{ "Header", "MessageType", "Message" })
// 指定序列成的xml節點順序
-
@XmlAccessorType(value = XmlAccessType.FIELD)
// 訪問類型改為字段
-
@XmlRootElement(name = "AmazonEnvelope")
-
public
class AmazonEnvelope {
-
@XmlElement
-
private Header Header;
//構造頭部
-
-
@XmlElement
-
private String MessageType;
-
-
-
@XmlElement
-
private Message Message;
-
-
-
public Header getHeader() {
-
return Header;
-
}
-
-
-
public void setHeader(Header header) {
-
Header = header;
-
}
-
-
-
public String getMessageType() {
-
return MessageType;
-
}
-
-
-
public void setMessageType(String messageType) {
-
MessageType = messageType;
-
}
-
-
-
public Message getMessage() {
-
return Message;
-
}
-
-
-
public void setMessage(Message message) {
-
Message = message;
-
}
-
}
-
package com.aukey.supply.chain.domain.test;
-
-
import javax.xml.bind.
annotation.XmlAccessType;
-
import javax.xml.bind.
annotation.XmlAccessorType;
-
import javax.xml.bind.
annotation.XmlElement;
-
import javax.xml.bind.
annotation.XmlType;
-
-
@XmlType(propOrder =
-
{ "MessageID", "ProcessingReport"})
// 指定序列成的xml節點順序
-
@XmlAccessorType(value = XmlAccessType.FIELD)
// 訪問類型改為字段
-
public
class Message {
-
-
@XmlElement
-
private String MessageID;
-
-
@XmlElement
-
private ProcessingReport ProcessingReport;
-
-
public String getMessageID() {
-
return MessageID;
-
}
-
-
public void setMessageID(String messageID) {
-
MessageID = messageID;
-
}
-
-
public ProcessingReport getProcessingReport() {
-
return ProcessingReport;
-
}
-
-
public void setProcessingReport(ProcessingReport processingReport) {
-
ProcessingReport = processingReport;
-
}
-
-
}
對象封裝完成之后,API一般需要請求參數,因此我們建完實體對象后,需要按照不同節點要求賦值,示例如下:
-
/**
-
* 構造XML對象 將節點數據組裝成一個XML大對象
-
* @return
-
*/
-
public
static AmazonEnvelope createXmlObject()
-
{
-
AmazonEnvelope amazonEnvelope =
new AmazonEnvelope();
-
-
//子級節點1
-
Header header =
new Header();
-
header.setDocumentVersion(
"1.02");
-
header.setMerchantIdentifier(
"A23G8Q8ZIKBK8C");
-
//賦值子級節點1
-
amazonEnvelope.setHeader(header);
-
-
//子級節點1
-
String messageType=
"ProcessingReport";
-
//賦值子級節點1
-
amazonEnvelope.setMessageType(messageType);
-
-
//子級節點1
-
Message message =
new Message();
-
//賦值子級節點2
-
message.setMessageID(
"1");
-
-
//子級節點2
-
ProcessingReport processingReport=
new ProcessingReport();
-
-
//賦值子級節點2
-
processingReport.setDocumentTransactionID(
"57320017876");
-
//賦值子級節點2
-
processingReport.setStatusCode(
"Complete");
-
-
//子級節點3
-
ProcessingSummary processingSummary =
new ProcessingSummary();
-
//賦值子級節點3
-
processingSummary.setMessagesProcessed(
"15");
-
//賦值子級節點3
-
processingSummary.setMessagesSuccessful(
"13");
-
//賦值子級節點3
-
processingSummary.setMessagesWithError(
"2");
-
//賦值子級節點3
-
processingSummary.setMessagesWithWarning(
"0");
-
-
//子級節點3
-
List<Result> results=
new ArrayList<>();
-
Result result =
new Result();
-
//賦值子級節點4
-
result.setMessageID(
"3");
-
//賦值子級節點4
-
result.setResultCode(
"Error");
-
//賦值子級節點4
-
result.setResultDescription(
"25");
-
//賦值子級節點4
-
result.setResultMessageCode(
"We are unable to process the XML feed because one or more items are invalid. Please re-submit the feed.");
-
-
//賦值子級節點3
-
results.add(result);
-
-
-
//賦值子級節點2
-
processingReport.setResult(results);
-
-
//賦值子級節點2
-
processingReport.setProcessingSummary(processingSummary);
-
-
//賦值子級節點2
-
message.setProcessingReport(processingReport);
-
-
//賦值子級節點1
-
amazonEnvelope.setMessage(message);
-
-
return amazonEnvelope;
-
}
對象賦值完成后,需要把當前的XML對象封裝整個XML,一般設置字符編碼等。 並且組裝成一個String 這里JAXBContext文本對象來完成:
-
/**
-
* 構造XML 報文對象
-
* @param amazonEnvelope
-
* @return
-
*/
-
public static String createXml(AmazonEnvelope amazonEnvelope)
-
{
-
JAXBContext context;
-
try {
-
context = JAXBContext.newInstance(amazonEnvelope.getClass());
-
Marshaller marshaller = context.createMarshaller();
-
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
true);
-
marshaller.setProperty(Marshaller.JAXB_ENCODING,
"UTF-8");
-
StringWriter writer =
new StringWriter();
-
marshaller.marshal(amazonEnvelope, writer);
-
String xml = writer.toString();
-
return xml;
-
}
catch (JAXBException e) {
-
e.printStackTrace();
-
}
-
return
"";
-
}
封裝XML完成之后,就可以調取第三方的API並DOM解析返回了,這里說明為了方便,將請求對象和解析對象置為同一個。下面看主類全套調用邏輯:
-
package com.aukey.supply.chain.web.test;
-
-
import java.io.StringReader;
-
import java.io.StringWriter;
-
import java.util.ArrayList;
-
import java.util.HashMap;
-
import java.util.List;
-
import java.util.Map;
-
-
import javax.xml.bind.JAXBContext;
-
import javax.xml.bind.JAXBException;
-
import javax.xml.bind.Marshaller;
-
import javax.xml.bind.Unmarshaller;
-
-
import org.dom4j.Document;
-
import org.dom4j.DocumentException;
-
import org.dom4j.DocumentHelper;
-
import org.dom4j.Element;
-
-
import com.alibaba.fastjson.JSON;
-
import com.aukey.supply.chain.domain.test.AmazonEnvelope;
-
import com.aukey.supply.chain.domain.test.Header;
-
import com.aukey.supply.chain.domain.test.Message;
-
import com.aukey.supply.chain.domain.test.ProcessingReport;
-
import com.aukey.supply.chain.domain.test.ProcessingSummary;
-
import com.aukey.supply.chain.domain.test.Result;
-
import com.aukey.supply.chain.utils.Md5Utils;
-
import com.aukey.supply.chain.utils.XMLPostUtils;
-
-
public
class TestAnalyzeXml {
-
public
static void main(
String[] args)
-
{
-
//組裝請求報文XML對象
-
AmazonEnvelope amazonEnvelope =createXmlObject();
-
-
//構造XML文本
-
String xml= createXml(amazonEnvelope);
-
-
try
-
{
-
//封裝請求報文 然后發送HTTP請求 然后將返回XML字符串 進行解析對應XML格式的節點對象 然后獲取對應的節點數據
-
String urlStr =
"http://info.edaeu.com/Api/";
-
String token=
"";
-
String md5;
-
try {
-
md5 =
Md5Utils.
ChangeMd5(token.substring(
0,
16) + xml + token.substring(
16,
32));
-
}
catch (
Exception e) {
-
md5 =
"";
-
}
-
String httpPost =
XMLPostUtils.httpPost(xml, urlStr+
"/"+md5);
-
JAXBContext getcontext =
JAXBContext.newInstance(amazonEnvelope.getClass());
-
Unmarshaller unmarshaller = getcontext.createUnmarshaller();
-
StringReader reader = new
StringReader(httpPost);
-
Object object=(
AmazonEnvelope)unmarshaller.unmarshal(reader);
-
}
catch (
JAXBException e1) {
-
e1.printStackTrace();
-
}
-
-
try{
-
Document document =
DocumentHelper.parseText(xml);
-
// 通過document對象獲取根節點
-
Element root = document.getRootElement();
-
Element message = root.element(
"Message");
-
Element processingReport = message.element(
"ProcessingReport");
-
@
SuppressWarnings(
"unchecked")
-
List<
Element> results = processingReport.elements(
"Result");
-
List<
Map<
String,
Object>> mapResultList=new
ArrayList<
Map<
String,
Object>>();
-
for (
Element element : results)
-
{
-
Map<
String,
Object>
map =new
HashMap<
String,
Object>();
-
map.put(
"MessageID",element.element(
"MessageID").getTextTrim());
-
map.put(
"ResultCode", element.element(
"ResultCode").getTextTrim());
-
map.put(
"ResultMessageCode",element.element(
"ResultMessageCode").getTextTrim());
-
map.put(
"ResultDescription", element.element(
"ResultDescription").getTextTrim());
-
mapResultList.add(
map);
-
}
-
System.out.
println(
JSON.toJSONString(mapResultList));
-
-
}
catch (
DocumentException e) {
-
e.printStackTrace();
-
}
-
-
-
-
}
-
-
-
-
}
以上獲取完數據,差不多解析調用就完成了。整個封裝XML並調用API,以及返回解析API返回的XML就完成了!
福利(附帶Http請求XML封裝工具類以及MD5加密類):
-
package com.aukey.supply.chain.utils;
-
-
import java.io.ByteArrayOutputStream;
-
import java.io.InputStream;
-
import java.io.OutputStream;
-
import java.io.StringReader;
-
import java.net.HttpURLConnection;
-
import java.net.URL;
-
-
import javax.xml.bind.JAXBContext;
-
import javax.xml.bind.Unmarshaller;
-
import javax.xml.parsers.SAXParserFactory;
-
import javax.xml.transform.Source;
-
import javax.xml.transform.sax.SAXSource;
-
-
import org.xml.sax.InputSource;
-
import org.xml.sax.XMLReader;
-
-
public
class XMLPostUtils
-
{
-
-
public
static
String httpPost(
String xml,
String urlStr)
-
{
-
try
-
{
-
URL url = new
URL(urlStr);
-
// 建立http連接
-
HttpURLConnection conn = (
HttpURLConnection) url.openConnection();
-
// 設置允許輸出
-
conn.setDoOutput(
true);
-
-
conn.setDoInput(
true);
-
-
// 設置不用緩存
-
conn.setUseCaches(
false);
-
// 設置傳遞方式
-
conn.setRequestMethod(
"POST");
-
// 設置維持長連接
-
conn.setRequestProperty(
"Connection",
"Keep-Alive");
-
// 設置文件字符集:
-
conn.setRequestProperty(
"Charset",
"UTF-8");
-
// 轉換為字節數組
-
byte[] data = xml.getBytes();
-
// 設置文件長度
-
conn.setRequestProperty(
"Content-Length",
String.valueOf(data.length));
-
// 設置文件類型:
-
conn.setRequestProperty(
"contentType",
"text/xml");
-
// 開始連接請求
-
conn.connect();
-
OutputStream out = conn.getOutputStream();
-
// 寫入請求的字符串
-
out.write(data);
-
out.flush();
-
out.close();
-
-
// 請求返回的狀態
-
if (conn.getResponseCode() ==
200)
-
{
-
// 請求返回的數據
-
InputStream
in = conn.getInputStream();
-
try
-
{
-
ByteArrayOutputStream s = new
ByteArrayOutputStream();
-
int length =
0;
-
byte[] buffer = new byte[
1024 *
1024];
-
while ((length =
in.read(buffer)) != -
1)
-
{
-
s.write(buffer,
0, length);
-
}
-
return s.
toString(
"UTF-8");
-
-
}
-
catch (
Exception e1)
-
{
-
e1.printStackTrace();
-
}
-
finally
-
{
-
in.close();
-
}
-
}
-
else
-
{
-
}
-
-
}
-
catch (
Exception e)
-
{
-
e.printStackTrace();
-
-
}
-
return null;
-
}
-
-
public
static <
T>
T convertXmlToJavaBean(
String xml,
Class<
T> t)
throws
Exception
-
{
-
T obj;
-
JAXBContext context =
JAXBContext.newInstance(t);
-
StringReader stringReader = new
StringReader(xml);
-
SAXParserFactory sax =
SAXParserFactory.newInstance();
-
sax.setNamespaceAware(
false);
// 設置忽略明明空間
-
XMLReader xmlReader = sax.newSAXParser().getXMLReader();
-
Source source = new
SAXSource(xmlReader, new
InputSource(stringReader));
-
Unmarshaller unmarshaller = context.createUnmarshaller();
-
obj = (
T) unmarshaller.unmarshal(source);
-
return obj;
-
}
-
-
}
-
package com.aukey.task.centerwarehouse.utils;
-
-
import java.security.MessageDigest;
-
import java.security.NoSuchAlgorithmException;
-
-
public
class Md5Utils
-
{
-
public static String ChangeMd5(String password)
-
{
-
-
try
-
{
-
// 得到一個信息摘要器
-
MessageDigest digest = MessageDigest.getInstance(
"md5");
-
byte[] result = digest.digest(password.getBytes());
-
StringBuffer buffer =
new StringBuffer();
-
// 把每一個byte 做一個與運算 0xff;
-
for (
byte b : result)
-
{
-
// 與運算
-
int number = b &
0xff;
// 加鹽
-
String str = Integer.toHexString(number);
-
if (str.length() ==
1)
-
{
-
buffer.append(
"0");
-
}
-
buffer.append(str);
-
}
-
-
// 標准的md5加密后的結果
-
return buffer.toString();
-
}
-
catch (NoSuchAlgorithmException e)
-
{
-
e.printStackTrace();
-
return
"";
-
}
-
-
}
-
}