WebService如何封装XML请求 以及解析接口返回的XML
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 "";
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗