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 "";
 -         }
 -  
 -     }
 - }