zoukankan      html  css  js  c++  java
  • WebService如何封装XML请求 以及解析接口返回的XML

    WebService如何封装XML请求 以及解析接口返回的XML

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/qq_24818689/article/details/99677155

    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:

    1. /*
    2. * <?xml version="1.0" encoding="UTF-8"?>
    3. * <AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
    4. * <Header>
    5. * <DocumentVersion>1.02</DocumentVersion>
    6. * <MerchantIdentifier>A23G8Q8ZIKBK8C</MerchantIdentifier>
    7. * </Header>
    8. * <MessageType>ProcessingReport</MessageType>
    9. * <Message>
    10. * <MessageID>1</MessageID>
    11. * <ProcessingReport>
    12. * <DocumentTransactionID>57320017876</DocumentTransactionID>
    13. * <StatusCode>Complete</StatusCode>
    14. * <ProcessingSummary>
    15. * <MessagesProcessed>15</MessagesProcessed>
    16. * <MessagesSuccessful>13</MessagesSuccessful>
    17. * <MessagesWithError>2</MessagesWithError>
    18. * <MessagesWithWarning>0</MessagesWithWarning>
    19. * </ProcessingSummary>
    20. * <Result>
    21. * <MessageID>3</MessageID>
    22. * <ResultCode>Error</ResultCode>
    23. * <ResultMessageCode>25</ResultMessageCode>
    24. * <ResultDescription>We are unable to process the XML feed because one or more items are invalid. Please re-submit the feed.</ResultDescription>
    25. * </Result>
    26. * <Result>
    27. * <MessageID>4</MessageID>
    28. * <ResultCode>Error</ResultCode>
    29. * <ResultMessageCode>25</ResultMessageCode>
    30. * <ResultDescription>We are unable to process the XML feed because one or more items are invalid. Please re-submit the feed.</ResultDescription>
    31. * </Result>
    32. * </ProcessingReport>
    33. * </Message>
    34. * </AmazonEnvelope>
    35. */

            如果看到这种XML格式,去封装请求对象如何封装呢?

            我们如果有了解过XML这种语言就知道,XML可以理解为一颗树,有父子根节点构成。其实Spring 内部去解析XML时,也是根据这种特性去解析的。因为我们最原始MVC 需要大量的配置XML 注入bean。以及配置事物等等。我们通过分析可以发现,外部根节点为AmazonEnvelope,子节点Header、MessageType、Message,然后Message节点下又有子节点MessageID、ProcessingReport。依次类推,可以构造AmazonEnvelope大对象,然后以此为根节点建造子节点对象,这里举例两个如下:

    1. package com.aukey.supply.chain.domain.test;
    2. import javax.xml.bind.annotation.XmlAccessType;
    3. import javax.xml.bind.annotation.XmlAccessorType;
    4. import javax.xml.bind.annotation.XmlElement;
    5. import javax.xml.bind.annotation.XmlRootElement;
    6. import javax.xml.bind.annotation.XmlType;
    7. @XmlType(propOrder =
    8. { "Header", "MessageType", "Message" }) // 指定序列成的xml节点顺序
    9. @XmlAccessorType(value = XmlAccessType.FIELD) // 访问类型改为字段
    10. @XmlRootElement(name = "AmazonEnvelope")
    11. public class AmazonEnvelope {
    12. @XmlElement
    13. private Header Header;//构造头部
    14. @XmlElement
    15. private String MessageType;
    16. @XmlElement
    17. private Message Message;
    18. public Header getHeader() {
    19. return Header;
    20. }
    21. public void setHeader(Header header) {
    22. Header = header;
    23. }
    24. public String getMessageType() {
    25. return MessageType;
    26. }
    27. public void setMessageType(String messageType) {
    28. MessageType = messageType;
    29. }
    30. public Message getMessage() {
    31. return Message;
    32. }
    33. public void setMessage(Message message) {
    34. Message = message;
    35. }
    36. }
    1. package com.aukey.supply.chain.domain.test;
    2. import javax.xml.bind.annotation.XmlAccessType;
    3. import javax.xml.bind.annotation.XmlAccessorType;
    4. import javax.xml.bind.annotation.XmlElement;
    5. import javax.xml.bind.annotation.XmlType;
    6. @XmlType(propOrder =
    7. { "MessageID", "ProcessingReport"}) // 指定序列成的xml节点顺序
    8. @XmlAccessorType(value = XmlAccessType.FIELD) // 访问类型改为字段
    9. public class Message {
    10. @XmlElement
    11. private String MessageID;
    12. @XmlElement
    13. private ProcessingReport ProcessingReport;
    14. public String getMessageID() {
    15. return MessageID;
    16. }
    17. public void setMessageID(String messageID) {
    18. MessageID = messageID;
    19. }
    20. public ProcessingReport getProcessingReport() {
    21. return ProcessingReport;
    22. }
    23. public void setProcessingReport(ProcessingReport processingReport) {
    24. ProcessingReport = processingReport;
    25. }
    26. }

    对象封装完成之后,API一般需要请求参数,因此我们建完实体对象后,需要按照不同节点要求赋值,示例如下:

    1. /**
    2. * 构造XML对象 将节点数据组装成一个XML大对象
    3. * @return
    4. */
    5. public static AmazonEnvelope createXmlObject()
    6. {
    7. AmazonEnvelope amazonEnvelope =new AmazonEnvelope();
    8. //子级节点1
    9. Header header =new Header();
    10. header.setDocumentVersion("1.02");
    11. header.setMerchantIdentifier("A23G8Q8ZIKBK8C");
    12. //赋值子级节点1
    13. amazonEnvelope.setHeader(header);
    14. //子级节点1
    15. String messageType="ProcessingReport";
    16. //赋值子级节点1
    17. amazonEnvelope.setMessageType(messageType);
    18. //子级节点1
    19. Message message =new Message();
    20. //赋值子级节点2
    21. message.setMessageID("1");
    22. //子级节点2
    23. ProcessingReport processingReport=new ProcessingReport();
    24. //赋值子级节点2
    25. processingReport.setDocumentTransactionID("57320017876");
    26. //赋值子级节点2
    27. processingReport.setStatusCode("Complete");
    28. //子级节点3
    29. ProcessingSummary processingSummary =new ProcessingSummary();
    30. //赋值子级节点3
    31. processingSummary.setMessagesProcessed("15");
    32. //赋值子级节点3
    33. processingSummary.setMessagesSuccessful("13");
    34. //赋值子级节点3
    35. processingSummary.setMessagesWithError("2");
    36. //赋值子级节点3
    37. processingSummary.setMessagesWithWarning("0");
    38. //子级节点3
    39. List<Result> results=new ArrayList<>();
    40. Result result =new Result();
    41. //赋值子级节点4
    42. result.setMessageID("3");
    43. //赋值子级节点4
    44. result.setResultCode("Error");
    45. //赋值子级节点4
    46. result.setResultDescription("25");
    47. //赋值子级节点4
    48. result.setResultMessageCode("We are unable to process the XML feed because one or more items are invalid. Please re-submit the feed.");
    49. //赋值子级节点3
    50. results.add(result);
    51. //赋值子级节点2
    52. processingReport.setResult(results);
    53. //赋值子级节点2
    54. processingReport.setProcessingSummary(processingSummary);
    55. //赋值子级节点2
    56. message.setProcessingReport(processingReport);
    57. //赋值子级节点1
    58. amazonEnvelope.setMessage(message);
    59. return amazonEnvelope;
    60. }

    对象赋值完成后,需要把当前的XML对象封装整个XML,一般设置字符编码等。 并且组装成一个String 这里JAXBContext文本对象来完成:

    1. /**
    2. * 构造XML 报文对象
    3. * @param amazonEnvelope
    4. * @return
    5. */
    6. public static String createXml(AmazonEnvelope amazonEnvelope)
    7. {
    8. JAXBContext context;
    9. try {
    10. context = JAXBContext.newInstance(amazonEnvelope.getClass());
    11. Marshaller marshaller = context.createMarshaller();
    12. marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    13. marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
    14. StringWriter writer = new StringWriter();
    15. marshaller.marshal(amazonEnvelope, writer);
    16. String xml = writer.toString();
    17. return xml;
    18. } catch (JAXBException e) {
    19. e.printStackTrace();
    20. }
    21. return "";
    22. }

    封装XML完成之后,就可以调取第三方的API并DOM解析返回了,这里说明为了方便,将请求对象和解析对象置为同一个。下面看主类全套调用逻辑:

    1. package com.aukey.supply.chain.web.test;
    2. import java.io.StringReader;
    3. import java.io.StringWriter;
    4. import java.util.ArrayList;
    5. import java.util.HashMap;
    6. import java.util.List;
    7. import java.util.Map;
    8. import javax.xml.bind.JAXBContext;
    9. import javax.xml.bind.JAXBException;
    10. import javax.xml.bind.Marshaller;
    11. import javax.xml.bind.Unmarshaller;
    12. import org.dom4j.Document;
    13. import org.dom4j.DocumentException;
    14. import org.dom4j.DocumentHelper;
    15. import org.dom4j.Element;
    16. import com.alibaba.fastjson.JSON;
    17. import com.aukey.supply.chain.domain.test.AmazonEnvelope;
    18. import com.aukey.supply.chain.domain.test.Header;
    19. import com.aukey.supply.chain.domain.test.Message;
    20. import com.aukey.supply.chain.domain.test.ProcessingReport;
    21. import com.aukey.supply.chain.domain.test.ProcessingSummary;
    22. import com.aukey.supply.chain.domain.test.Result;
    23. import com.aukey.supply.chain.utils.Md5Utils;
    24. import com.aukey.supply.chain.utils.XMLPostUtils;
    25. public class TestAnalyzeXml {
    26. public static void main(String[] args)
    27. {
    28. //组装请求报文XML对象
    29. AmazonEnvelope amazonEnvelope =createXmlObject();
    30. //构造XML文本
    31. String xml= createXml(amazonEnvelope);
    32. try
    33. {
    34. //封装请求报文 然后发送HTTP请求 然后将返回XML字符串 进行解析对应XML格式的节点对象 然后获取对应的节点数据
    35. String urlStr = "http://info.edaeu.com/Api/";
    36. String token="";
    37. String md5;
    38. try {
    39. md5 = Md5Utils.ChangeMd5(token.substring(0, 16) + xml + token.substring(16, 32));
    40. } catch (Exception e) {
    41. md5 = "";
    42. }
    43. String httpPost = XMLPostUtils.httpPost(xml, urlStr+"/"+md5);
    44. JAXBContext getcontext = JAXBContext.newInstance(amazonEnvelope.getClass());
    45. Unmarshaller unmarshaller = getcontext.createUnmarshaller();
    46. StringReader reader = new StringReader(httpPost);
    47. Object object=(AmazonEnvelope)unmarshaller.unmarshal(reader);
    48. } catch (JAXBException e1) {
    49. e1.printStackTrace();
    50. }
    51. try{
    52. Document document = DocumentHelper.parseText(xml);
    53. // 通过document对象获取根节点
    54. Element root = document.getRootElement();
    55. Element message = root.element("Message");
    56. Element processingReport = message.element("ProcessingReport");
    57. @SuppressWarnings("unchecked")
    58. List<Element> results = processingReport.elements("Result");
    59. List<Map<String, Object>> mapResultList=new ArrayList<Map<String,Object>>();
    60. for (Element element : results)
    61. {
    62. Map<String, Object> map =new HashMap<String, Object>();
    63. map.put("MessageID",element.element("MessageID").getTextTrim());
    64. map.put("ResultCode", element.element("ResultCode").getTextTrim());
    65. map.put("ResultMessageCode",element.element("ResultMessageCode").getTextTrim());
    66. map.put("ResultDescription", element.element("ResultDescription").getTextTrim());
    67. mapResultList.add(map);
    68. }
    69. System.out.println(JSON.toJSONString(mapResultList));
    70. } catch (DocumentException e) {
    71. e.printStackTrace();
    72. }
    73. }
    74. }

    以上获取完数据,差不多解析调用就完成了。整个封装XML并调用API,以及返回解析API返回的XML就完成了!

    福利(附带Http请求XML封装工具类以及MD5加密类):

    1. package com.aukey.supply.chain.utils;
    2. import java.io.ByteArrayOutputStream;
    3. import java.io.InputStream;
    4. import java.io.OutputStream;
    5. import java.io.StringReader;
    6. import java.net.HttpURLConnection;
    7. import java.net.URL;
    8. import javax.xml.bind.JAXBContext;
    9. import javax.xml.bind.Unmarshaller;
    10. import javax.xml.parsers.SAXParserFactory;
    11. import javax.xml.transform.Source;
    12. import javax.xml.transform.sax.SAXSource;
    13. import org.xml.sax.InputSource;
    14. import org.xml.sax.XMLReader;
    15. public class XMLPostUtils
    16. {
    17. public static String httpPost(String xml, String urlStr)
    18. {
    19. try
    20. {
    21. URL url = new URL(urlStr);
    22. // 建立http连接
    23. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    24. // 设置允许输出
    25. conn.setDoOutput(true);
    26. conn.setDoInput(true);
    27. // 设置不用缓存
    28. conn.setUseCaches(false);
    29. // 设置传递方式
    30. conn.setRequestMethod("POST");
    31. // 设置维持长连接
    32. conn.setRequestProperty("Connection", "Keep-Alive");
    33. // 设置文件字符集:
    34. conn.setRequestProperty("Charset", "UTF-8");
    35. // 转换为字节数组
    36. byte[] data = xml.getBytes();
    37. // 设置文件长度
    38. conn.setRequestProperty("Content-Length", String.valueOf(data.length));
    39. // 设置文件类型:
    40. conn.setRequestProperty("contentType", "text/xml");
    41. // 开始连接请求
    42. conn.connect();
    43. OutputStream out = conn.getOutputStream();
    44. // 写入请求的字符串
    45. out.write(data);
    46. out.flush();
    47. out.close();
    48. // 请求返回的状态
    49. if (conn.getResponseCode() == 200)
    50. {
    51. // 请求返回的数据
    52. InputStream in = conn.getInputStream();
    53. try
    54. {
    55. ByteArrayOutputStream s = new ByteArrayOutputStream();
    56. int length = 0;
    57. byte[] buffer = new byte[1024 * 1024];
    58. while ((length = in.read(buffer)) != -1)
    59. {
    60. s.write(buffer, 0, length);
    61. }
    62. return s.toString("UTF-8");
    63. }
    64. catch (Exception e1)
    65. {
    66. e1.printStackTrace();
    67. }
    68. finally
    69. {
    70. in.close();
    71. }
    72. }
    73. else
    74. {
    75. }
    76. }
    77. catch (Exception e)
    78. {
    79. e.printStackTrace();
    80. }
    81. return null;
    82. }
    83. public static <T> T convertXmlToJavaBean(String xml, Class<T> t) throws Exception
    84. {
    85. T obj;
    86. JAXBContext context = JAXBContext.newInstance(t);
    87. StringReader stringReader = new StringReader(xml);
    88. SAXParserFactory sax = SAXParserFactory.newInstance();
    89. sax.setNamespaceAware(false);// 设置忽略明明空间
    90. XMLReader xmlReader = sax.newSAXParser().getXMLReader();
    91. Source source = new SAXSource(xmlReader, new InputSource(stringReader));
    92. Unmarshaller unmarshaller = context.createUnmarshaller();
    93. obj = (T) unmarshaller.unmarshal(source);
    94. return obj;
    95. }
    96. }
    1. package com.aukey.task.centerwarehouse.utils;
    2. import java.security.MessageDigest;
    3. import java.security.NoSuchAlgorithmException;
    4. public class Md5Utils
    5. {
    6. public static String ChangeMd5(String password)
    7. {
    8. try
    9. {
    10. // 得到一个信息摘要器
    11. MessageDigest digest = MessageDigest.getInstance("md5");
    12. byte[] result = digest.digest(password.getBytes());
    13. StringBuffer buffer = new StringBuffer();
    14. // 把每一个byte 做一个与运算 0xff;
    15. for (byte b : result)
    16. {
    17. // 与运算
    18. int number = b & 0xff;// 加盐
    19. String str = Integer.toHexString(number);
    20. if (str.length() == 1)
    21. {
    22. buffer.append("0");
    23. }
    24. buffer.append(str);
    25. }
    26. // 标准的md5加密后的结果
    27. return buffer.toString();
    28. }
    29. catch (NoSuchAlgorithmException e)
    30. {
    31. e.printStackTrace();
    32. return "";
    33. }
    34. }
    35. }
  • 相关阅读:
    【C语言】学习笔记5——指针(1)
    【C语言】学习笔记4——数组
    【leetcode】Contest98
    【Python】从0开始写爬虫——豆瓣电影
    C# WPF 文件复制,相对路径
    WPF DataGrid多表头/列头,多行头,合并单元格,一列占据多行
    WPF Image Source 设置相对路径图片
    WPF Blend 一个动画结束后另一个动画开始执行(一个一个执行)
    WPF 操作XML 读写
    WPF 选择电脑文件显示路径,弹出资源管理器,打开文件
  • 原文地址:https://www.cnblogs.com/LoveShare/p/11388708.html
Copyright © 2011-2022 走看看