WebService如何封装XML请求 以及解析接口返回的XML
版权声明:本文为博主原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接和本声明。
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 "";
-
}
-
-
}
-
}