• 调用需求Http Basic身份验证的SAP Webservice


    背景

    笔者当前从事制造业相关行业软件工作,因工作需要,在MES系统中需要向SAP拉取订单、物料、工序等数据。

    笔者接触的SAP提供了跨系统通信的Po中间件(实际上是WebService SOAP1.0)。

    通常情况下,我们只要使用wsdl生成工具,生成本地的调用客户端即可。常用的客户端工具是apace-cxf。

    IDEA旗舰版本中,邮件项目,有个WebService相关的菜单,输入wsdl地址即可。社区版一般安装一个maven插件,如下

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>3.4.5</version>
                <executions>
                    <execution>
                        <id>generate-sources-w2j</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <sourceRoot>src/main/java</sourceRoot>
                            <defaultOptions>
                                <extraargs>
                                    <extraarg>-impl</extraarg>
                                    <extraarg>-verbose</extraarg>
                                    <extraarg>-validate</extraarg>
                                    <!--<extraarg>-client</extraarg>-->
                                </extraargs>
                            </defaultOptions>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>http://www1.host.com/dir/wsdl?p=ic/2f1826f53a9b398a83000fdd7a05814e</wsdl>
                                    <extendedSoapHeaders>true</extendedSoapHeaders>
                                    <autoNameResolution>true</autoNameResolution>
                                </wsdlOption>
                                <!-- MES向SAP 按时间读取物料主数据接口-->
                                <wsdlOption>
                                    <wsdl>http://www1.host.com/dir/wsdl?p=ic/fb71dee0a5c538d99d305c6f28eebd53</wsdl>
                                    <extendedSoapHeaders>true</extendedSoapHeaders>
                                    <autoNameResolution>true</autoNameResolution>
                                </wsdlOption>
                                <!-- MES向SAP 生产报工同步接口 -->
                                <wsdlOption>
                                    <wsdl>http://www1.host.com/dir/wsdl?p=ic/46d3848617fa3dd7bc806f248d51340f</wsdl>
                                    <extendedSoapHeaders>true</extendedSoapHeaders>
                                    <autoNameResolution>true</autoNameResolution>
                                </wsdlOption>
                                <!-- MES向SAP 按物料编号读取物料主数据接口 -->
                                <wsdlOption>
                                    <wsdl>http://www1.host.com/dir/wsdl?p=ic/48c28c49f4fc337d9fdfc3d5feee4e66</wsdl>
                                    <extendedSoapHeaders>true</extendedSoapHeaders>
                                    <autoNameResolution>true</autoNameResolution>
                                </wsdlOption>
                                <!--派工单下达传输-->
                                <wsdlOption>
                                    <wsdl>http://www1.host.com/dir/wsdl?p=ic/6dcd80497ea937c8b484635b554710f4</wsdl>
                                    <extendedSoapHeaders>true</extendedSoapHeaders>
                                    <autoNameResolution>true</autoNameResolution>
                                </wsdlOption>
                                <!--工单开工标识-->
                                <wsdlOption>
                                    <wsdl>http://www1.host.com/dir/wsdl?p=ic/4c602e2c4d673d3ba71a205d708b9c02</wsdl>
                                    <extendedSoapHeaders>true</extendedSoapHeaders>
                                    <autoNameResolution>true</autoNameResolution>
                                </wsdlOption>
                                <!-- MES向SAP 工单信息读取 -->
                                <wsdlOption>
                                    <wsdl>http://www1.host.com/dir/wsdl?p=ic/40dd063793613e2caec3d96510577b37</wsdl>
                                    <extendedSoapHeaders>true</extendedSoapHeaders>
                                    <autoNameResolution>true</autoNameResolution>
                                </wsdlOption>
                            </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </plugins>

    然后在右侧点击wsdl2java即可生成本地客户端调用代码

     这是一是常规情况,SAP的特殊支持就是在通信协议层面做了Http Basic认证,即在请求头要添加

    Authorization: Basic base64字符串

    以下是物料按时间拉取的一个标准请求报文及相应报文,使用soapui发包,通过wireshark抓取

    POST /XISOAPAdapter/MessageServlet?senderParty=&senderService=BS_MES_DEV&receiverParty=&receiverService=
    &interface=SI_MaterialMasterData_In&interfaceNamespace=http://host.com/MES/MaterialMasterData/Sender HTTP/1.1 Accept-Encoding: gzip,deflate Content-Type: text/xml;charset=UTF-8 SOAPAction: "http://sap.com/xi/WebService/soap1.1" Content-Length: 450 Host: www1.host.com Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.5 (Java/15) Authorization: Basic VmlzaXRvcjpxd2VyMTIzNA== <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:sap-com:document:sap:rfc:functions"> <soapenv:Header/> <soapenv:Body> <urn:ZMES_SAP_003> <!--You may enter the following 4 items in any order--> <!--Optional:--> <BEDAT>20211210</BEDAT> <!--Optional:--> <EDDAT>20211212</EDDAT> </urn:ZMES_SAP_003> </soapenv:Body> </soapenv:Envelope>HTTP/1.1 200 OK server: SAP NetWeaver Application Server 7.49 / AS Java 7.50 date: Thu, 23 Dec 2021 07:41:51 GMT set-cookie: MYSAPSSO2=AjExMDAgAA5wb3J0YWw6dmlzaXRvcogAB2RlZmF1bHQBAAdWSVNJVE9SAgADMDAwAwADUE9EBAAMMjAyMTEyMjMwNz
    QxBQAEAAAACAoAB1ZJU0lUT1L%2FAQYwggECBgkqhkiG9w0BBwKggfQwgfECAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3DQEHATGB0TCBzgIBAT
    AiMB0xDDAKBgNVBAMTA1BPRDENMAsGA1UECxMESjJFRQIBADAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG
    9w0BCQUxDxcNMjExMjIzMDc0MTUxWjAjBgkqhkiG9w0BCQQxFgQUdWxoAzhEYiSIc27jSe8FsKCEbjswCQYHKoZIzjgEAwQwMC4CFQDLMD
    BWN7xZHY!5EWvCn3aesvvO6wIVAJWRZf5SLYj6KJlD!DaUx%2F!D3X!H;path=/;domain=.host.com;HttpOnly content-type: text/xml; charset=utf-8 content-id:
    <soap-cb1e8be463c311ec92be0000002094d2@sap.com> content-disposition: attachment;filename="soap-cb1e8be463c311ec92be0000002094d2@sap.com.xml" content-description: SOAP content-encoding: gzip content-length: 557 set-cookie: saplb_*=(J2EE2135220)2135250; Version=1; Path=/ set-cookie: JSESSIONID=lvNdixDYPSn8vdeDR9UxlrxfOT3mfQHSlCAA_SAPZmasLjjtl7ew9y4UdoHLQTn2; Version=1; Path=/ set-cookie: JSESSIONMARKID=9EvTHA-TE05LwidImrSEHzSGbAroysc13JPdKUIAA; Version=1; Path=/ <SOAP:Envelope xmlns:SOAP='http://schemas.xmlsoap.org/soap/envelope/'><SOAP:Header/><SOAP:Body>
    <
    ns0:ZMES_SAP_003.Response xmlns:ns0='urn:sap-com:document:sap:rfc:functions'><MESSAGE>查询成功</MESSAGE>
    <
    STA>S</STA><IT_MARA><item><MATNR>A06010475</MATNR><MAKTX>卡扣 φ6.3-φ7 白</MAKTX><MTART>Z001</MTART>
    <
    MEINS>ST</MEINS><MATKL>3001</MATKL><GROES/><BISMT/><PRDHA/><MFRNR>0000200021</MFRNR><MFRPN/>
    <
    ZEXTRA1/><ZEXTRA2>C</ZEXTRA2><ZEXTRA3>18</ZEXTRA3><ZEXTRA4>总装领料</ZEXTRA4><ZEXTRA5>0</ZEXTRA5>
    <
    ZEXTRA6/><ZEXTRA7/><ZEXTRA8>180</ZEXTRA8><ZEXTRA9/><ZEXTRA10/><ZEXTRA13/></item></IT_MARA><IT_MARC>
    <
    item><MATNR>A06010475</MATNR><WERKS>1201</WERKS><EKGRP/><BSTMI>0</BSTMI><BESKZ/><SOBSL/><RGEKM/>
    <
    DZEIT>0</DZEIT><PLIFZ>0</PLIFZ><EISBE>0</EISBE><MAABC/><FEVOR/><BSTRF>0</BSTRF></item><item>
    <
    MATNR>A06010475</MATNR><WERKS>1101</WERKS><EKGRP>101</EKGRP><BSTMI>0</BSTMI><BESKZ>F</BESKZ>
    <
    SOBSL/><RGEKM/><DZEIT>0</DZEIT><PLIFZ>5</PLIFZ><EISBE>0</EISBE><MAABC/><FEVOR/><BSTRF>12000.000</BSTRF>
    </
    item><item><MATNR>A06010475</MATNR><WERKS>1001</WERKS><EKGRP>101</EKGRP><BSTMI>1000.000</BSTMI>
    <
    BESKZ>F</BESKZ><SOBSL/><RGEKM/><DZEIT>0</DZEIT><PLIFZ>30</PLIFZ><EISBE>0</EISBE><MAABC/><FEVOR/>
    <
    BSTRF>1000.000</BSTRF></item></IT_MARC></ns0:ZMES_SAP_003.Response></SOAP:Body></SOAP:Envelope>

    常规情况下apache cxf生成的客户端是不能带自定义请求头的,payload可以是自定义。当然网上有注册一个bean,

    代理cxf本地请求客户端,带入身份验证,笔者认为笔记麻烦,没去测试。

    解决方案

    这篇文章九不科普太多了,直接上解决方案

    SOAPClient.java
    import cn.hutool.core.annotation.AnnotationUtil;
    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.type.TypeFactory;
    import com.fasterxml.jackson.dataformat.xml.XmlMapper;
    import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
    import com.xxx.mes.common.exception.SAPCommunicationException;
    import com.xxx.mes.webservice.XPathExpression;
    import lombok.extern.slf4j.Slf4j;
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Node;
    import org.dom4j.io.SAXReader;
    import org.dom4j.xpath.DefaultXPath;
    import org.springframework.http.HttpStatus;
    
    import javax.xml.bind.annotation.XmlRootElement;
    import java.io.*;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.Base64;
    import java.util.Collections;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    import static com.xxx.mes.common.util.XmlUtil.JaxbBinder.fromXml;
    
    /***
     * SOAP客户端<br/>
     * 可以发送报文,解析报文映射成实体类
     *
     */
    @Slf4j
    public final class SOAPClient {
    
        private static final SAXReader saxReader = new SAXReader();
    
        private static final Map<Class<?>,String> annotatedClassCache = new LinkedHashMap<Class<?>,String>();
    
        /***
         * 从基于soap的webservice报文中提取响应结果
         * @param xml 返回的原始报文
         * @param expression xpath评估表达式
         * @param targetClass 待返回的结果
         * @param <T>
         * @return 如果解析成功将返回一个<code>T</code>类的响应实体
         * @throws DocumentException
         */
        public static <T> T extractResponse(String xml, XPathExpression expression, Class<T> targetClass) throws Exception {
    
            Document doc = saxReader.read(new ByteArrayInputStream(xml.getBytes()));
            DefaultXPath xpath = new DefaultXPath(expression.getExpression());
            xpath.setNamespaceURIs(Collections.singletonMap(expression.getNamespaceURI(), expression.getTargetNamespace()));
            Node node = xpath.selectSingleNode(doc);
            final T response = fromXml(targetClass, node.asXML());
            return response;
        }
    
        public static String getServiceUrl(String serviceName) {
            return String.format("http://www1.host.com/XISOAPAdapter/MessageServlet?senderParty=&senderService=BS_MES_DEV" +
                            "&receiverParty=&receiverService=&interface=SI_%s_In&interfaceNamespace=http://host.com/MES/%s/Sender"
                    , new Object[]{serviceName, serviceName});
        }
    
        /***
         * 通过soap请求载荷构造soap请求报文
         * @param payload
         * @param <T>
         * @return xml-based soap request payload
         * @throws JsonProcessingException
         */
        public static <T> String buildSOAPRequest(T payload) throws JsonProcessingException {
            final Class<?> targetClass = payload.getClass();
            String elementName = annotatedClassCache.get(targetClass);
    
    
            if(elementName == null){
                elementName = AnnotationUtil.getAnnotation(targetClass, XmlRootElement.class).name();
                annotatedClassCache.put(targetClass,elementName);
            }
    
            XmlMapper xmlMapper = (XmlMapper) new XmlMapper()
                    .setAnnotationIntrospector(new JaxbAnnotationIntrospector(TypeFactory.defaultInstance()));
    
            //字段为null,自动忽略,不再序列化
            xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    
            StringBuilder builder = new StringBuilder();
            builder.append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"urn:sap-com:document:sap:rfc:functions\">\n" +
                    "   <soapenv:Header/>\n" +
                    "   <soapenv:Body>");
            builder.append("<urn:").append(elementName).append(">");
            String xml = xmlMapper.writeValueAsString(payload);
            builder.append(xml);
            builder.append("</urn:").append(elementName).append(">").
                    append("   </soapenv:Body>").
                    append("</soapenv:Envelope>");
            log.info("xml报文:{}", builder);
            return builder.toString().replace("<" + elementName + ">", "").replace("</" + elementName + ">", "");
    
        }
    
        /***
         * 向SAP服务器提供的Webservice发起调用请求
         * @param httpUrl 请求地址
         * @param soapBody 使用{@link #buildSOAPRequest(Object)}构造的请求报文
         * @return 返回xml格式的响应报文
         * @throws IOException
         */
        public static String send(String httpUrl, String soapBody) throws IOException {
            String resultData = "";
            URL geturl = null;
            try {
                // 构造一个URL对象
                geturl = new URL(httpUrl);
            } catch (MalformedURLException e) {
                throw new SAPCommunicationException("url地址格式无效:%s", new Object[]{httpUrl});
            }
    
            try {
                // 使用HttpURLConnection打开连接
                HttpURLConnection urlConn = (HttpURLConnection) geturl.openConnection();
                // 设置请求的超时时间
                urlConn.setReadTimeout(50000);
                urlConn.setConnectTimeout(50000);
                // Windows验证 用户密码
                String datap = "Visitor:qwer1234";
    
                String authorization = Base64.getEncoder().encodeToString(datap.getBytes());
    
                urlConn.setRequestProperty("Authorization", "Basic " + authorization);
                // 设置请求的头
                urlConn.setRequestProperty("Connection", "keep-alive");
                // 配置本次连接的Content-type,配置为application/x-www-form-urlencoded的
                urlConn.setRequestProperty("Content-Type", "text/xml;charset=UTF-8");
    
                urlConn.setRequestProperty("SOAPAction", "http://sap.com/xi/WebService/soap1.1");
                // 设置请求的头
                urlConn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0");
                // 因为这个是post请求,设立需要设置为true
                urlConn.setDoOutput(true);
                urlConn.setDoInput(true);
                // 设置以POST方式
                urlConn.setRequestMethod("POST");
    
                // Post 请求不能使用缓存
                urlConn.setUseCaches(false);
                // 获取输出流
                // BufferedReader os = new BufferedReader(new
                // OutputStream(urlConn.getOutputStream()))
                OutputStream os = urlConn.getOutputStream();
                // byte[] content = data.getBytes("utf-8");
                os.write(soapBody.getBytes());
                os.flush();
                os.close();
                int statusCode = urlConn.getResponseCode();
                if (statusCode == HttpStatus.OK.value()) {
                    // 获取响应的输入流对象
                    InputStream is = urlConn.getInputStream();
                    // 创建字节输出流对象
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    // 定义读取的长度
                    int len;
                    int irecord = 1;
                    // 定义缓冲区
                    byte buffer[] = new byte[1024];
                    // 按照缓冲区的大小,循环读取
                    while ((len = is.read(buffer)) != -1) {
                        // 根据读取的长度写入到os对象中
                        baos.write(buffer, 0, len);
                        irecord += 1;
                    }
                    // 释放资源
                    is.close();
                    baos.close();
                    // 返回字符串
                    resultData = new String(baos.toByteArray());
                    irecord -= 1;
                    return resultData;
                } else {
                    urlConn.disconnect();
                    throw new SAPCommunicationException("接口报文返回异常:http状态码,期望值200,实际:%d", new Object[]{statusCode});
                }
            } catch (Exception e) {
                log.error("SAP通信异常",e);
                throw e;
            }
        }

    XPathExpression.java

    import lombok.Data;
    
    /**
     * 中间参数类,用于解析xml的xpath表达式<br/>
     * 数据样本:
     * <pre>
     *     <SOAP:Envelope
     *     xmlns:SOAP='http://schemas.xmlsoap.org/soap/envelope/'>
     *     <SOAP:Header/>
     *     <SOAP:Body>
     *         <ns0:ZMES_SAP_003.Response
     *             xmlns:ns0='urn:sap-com:document:sap:rfc:functions'>
     *             <MESSAGE>查询成功</MESSAGE>
     *             <STA>S</STA>
     *             <IT_MARA>
     *                 <item>
     *                     <MATNR>A06010475</MATNR>
     *                     <MAKTX>卡扣 φ6.3-φ7 白</MAKTX>
     *                     <MTART>Z001</MTART>
     *                     <MEINS>ST</MEINS>
     *                     <MATKL>3001</MATKL>
     *                     <GROES/>
     *                     <BISMT/>
     *                     <PRDHA/>
     *                     <MFRNR>0000200021</MFRNR>
     *                     <MFRPN/>
     *                     <ZEXTRA1/>
     *                     <ZEXTRA2>C</ZEXTRA2>
     *                     <ZEXTRA3>18</ZEXTRA3>
     *                     <ZEXTRA4>总装领料</ZEXTRA4>
     *                     <ZEXTRA5>0</ZEXTRA5>
     *                     <ZEXTRA6/>
     *                     <ZEXTRA7/>
     *                     <ZEXTRA8>180</ZEXTRA8>
     *                     <ZEXTRA9/>
     *                     <ZEXTRA10/>
     *                     <ZEXTRA13/>
     *                 </item>
     *             </IT_MARA>
     *             <IT_MARC>
     *                 <item>
     *                     <MATNR>A06010475</MATNR>
     *                     <WERKS>1201</WERKS>
     *                     <EKGRP/>
     *                     <BSTMI>0</BSTMI>
     *                     <BESKZ/>
     *                     <SOBSL/>
     *                     <RGEKM/>
     *                     <DZEIT>0</DZEIT>
     *                     <PLIFZ>0</PLIFZ>
     *                     <EISBE>0</EISBE>
     *                     <MAABC/>
     *                     <FEVOR/>
     *                     <BSTRF>0</BSTRF>
     *                 </item>
     *                 <item>
     *                     <MATNR>A06010475</MATNR>
     *                     <WERKS>1101</WERKS>
     *                     <EKGRP>101</EKGRP>
     *                     <BSTMI>0</BSTMI>
     *                     <BESKZ>F</BESKZ>
     *                     <SOBSL/>
     *                     <RGEKM/>
     *                     <DZEIT>0</DZEIT>
     *                     <PLIFZ>5</PLIFZ>
     *                     <EISBE>0</EISBE>
     *                     <MAABC/>
     *                     <FEVOR/>
     *                     <BSTRF>12000.000</BSTRF>
     *                 </item>
     *                 <item>
     *                     <MATNR>A06010475</MATNR>
     *                     <WERKS>1001</WERKS>
     *                     <EKGRP>101</EKGRP>
     *                     <BSTMI>1000.000</BSTMI>
     *                     <BESKZ>F</BESKZ>
     *                     <SOBSL/>
     *                     <RGEKM/>
     *                     <DZEIT>0</DZEIT>
     *                     <PLIFZ>30</PLIFZ>
     *                     <EISBE>0</EISBE>
     *                     <MAABC/>
     *                     <FEVOR/>
     *                     <BSTRF>1000.000</BSTRF>
     *                 </item>
     *             </IT_MARC>
     *         </ns0:ZMES_SAP_003.Response>
     *     </SOAP:Body>
     * </SOAP:Envelope>
     * </pre>
     *
     * @author: passedbylove
     * @date: Created by  2021/12/28 11:04
     * @version: 1.0.0
     */
    
    @Data
    public class XPathExpression {
        /***
         * xpath提取数据节点的表达式
         * eg.<br/>
         * //ns0:ZMES_SAP_003.Response
         */
        private String expression;
        /***
         * 命名空间的前缀
         * eg.<br/>
         * ns0
         */
        private String namespaceURI;
        /***
         * 命名空间
         * eg.<br/>
         * "urn:sap-com:document:sap:rfc:functions"
         */
        private String targetNamespace;
    
    
        public XPathExpression() {
        }
    
        public XPathExpression(String expression, String namespaceURI, String targetNamespace) {
            this.expression = expression;
            this.namespaceURI = namespaceURI;
            this.targetNamespace = targetNamespace;
        }
    }
    SAPCommunicationException.java
    import lombok.NoArgsConstructor;
    
    /**
     * 用于注明是SAP系统通信过程中产生的异常,区别于其他异常,比如调用SAP接口过程中可能产生
     * <code>UnknownHostException</</code>或者<code>IOException</code</code>等;此处归一化为<code>SAPCommunicationException</code>
     * 方便排错
     * @author: daiqiankun
     * @date: Created by  2021/12/28 11:11
     * @version: 1.0.0
     */
    @NoArgsConstructor
    public class SAPCommunicationException extends RuntimeException {
    
        public SAPCommunicationException(String message) {
            super(message);
        }
    
        public SAPCommunicationException(String format,Object[] args) {
            super(String.format(format,args));
        }
    
        public SAPCommunicationException(String message, Throwable ex) {
            super(message, ex);
        }
    
        public SAPCommunicationException(String message, Object[] args,Throwable ex) {
            super(String.format(message,args), ex);
        }
    }
    JaxbBinder.java
     /**
         * @datetime 2021/12/27 15:29
         */
        public static class JaxbBinder
        {
            /***
             * 缓存一下JAXBContext,减少反射开销
             */
            static Map<Class<?>, JAXBContext> contextCache = new LinkedHashMap<>();
    
            //多线程安全的Context.
            private JAXBContext jaxbContext;
    
            public JaxbBinder()
            {}
    
            /**
             * @param types 所有需要序列化的Root对象的类型.
             */
            public JaxbBinder(Class<?>... types) {
                try {
                    jaxbContext = JAXBContext.newInstance(types);
                } catch (JAXBException e) {
                    throw new RuntimeException(e);
                }
            }
    
            /**
             * Java Object->Xml.
             */
            public String toXml(Object root, String encoding) {
                try {
                    StringWriter writer = new StringWriter();
                    createMarshaller(encoding).marshal(root, writer);
                    return writer.toString();
                } catch (JAXBException e) {
                    throw new RuntimeException(e);
                }
            }
    
            /**
             * Java Object->Xml, 特别支持对Root Element是Collection的情形.
             */
            @SuppressWarnings("unchecked")
            public String toXml(Collection root, String rootName, String encoding) {
                try {
                    CollectionWrapper wrapper = new CollectionWrapper();
                    wrapper.collection = root;
    
                    JAXBElement<CollectionWrapper> wrapperElement = new JAXBElement<CollectionWrapper>(new QName(rootName),
                            CollectionWrapper.class, wrapper);
    
                    StringWriter writer = new StringWriter();
                    createMarshaller(encoding).marshal(wrapperElement, writer);
    
                    return writer.toString();
                } catch (JAXBException e) {
                    throw new RuntimeException(e);
                }
            }
    
            /**
             * Xml->Java Object.
             */
            @SuppressWarnings("unchecked")
            public <T> T fromXml(String xml) {
                try {
                    StringReader reader = new StringReader(xml);
                    return (T) createUnmarshaller().unmarshal(reader);
                } catch (JAXBException e) {
                    throw new RuntimeException(e);
                }
            }
    
            /**
             * 将XML转为指定的POJO
             *
             * @param clazz
             * @param xmlStr
             * @return
             * @throws Exception
             */
            public static <T> T fromXml(Class<?> clazz, String xmlStr) throws Exception {
                T xmlObject = null;
                Reader reader = null;
    
                JAXBContext context = null;
                context = contextCache.get(clazz);
    
                if (Objects.isNull(context)) {
                    context = JAXBContext.newInstance(clazz);
                    contextCache.put(clazz, context);
                }
                // XML 转为对象的接口
                Unmarshaller unmarshaller = context.createUnmarshaller();
                reader = new StringReader(xmlStr);
                //以文件流的方式传入这个string
                xmlObject = (T)unmarshaller.unmarshal(reader);
                if (null != reader) {
                    reader.close();
                }
                return xmlObject;
            }
    
            /**
             * 创建Marshaller, 设定encoding(可为Null).
             */
            public Marshaller createMarshaller(String encoding) {
                try {
                    Marshaller marshaller = jaxbContext.createMarshaller();
    
                    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    
                    if (!StringUtils.isEmpty(encoding)) {
                        marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
                    }
                    return marshaller;
                } catch (JAXBException e) {
                    throw new RuntimeException(e);
                }
            }
    
            /**
             * 创建UnMarshaller.
             */
            public Unmarshaller createUnmarshaller() {
                try {
                    return jaxbContext.createUnmarshaller();
                } catch (JAXBException e) {
                    throw new RuntimeException(e);
                }
            }
    
            /**
             * 封装Root Element 是 Collection的情况.
             */
            public static class CollectionWrapper {
                @SuppressWarnings("unchecked")
                @XmlAnyElement
                protected Collection collection;
            }
    
    
            @SuppressWarnings("unchecked")
            public <T> T fromXML(String fileName) {
                return (T)fromXML(new File(fileName));
            }
    
    
            @SuppressWarnings("unchecked")
            public <T> T fromXML(File file) {
                try {
                    Unmarshaller unmarshaller = createUnmarshaller();
                    return (T) unmarshaller.unmarshal(file);
                } catch (JAXBException e) {
                    throw new RuntimeException(e);
                }
            }
    
    
            @SuppressWarnings("unchecked")
            public <T> T fromXML(InputStream stream) {
                try {
                    Unmarshaller unmarshaller = createUnmarshaller();
                    return (T) unmarshaller.unmarshal(stream);
                } catch (JAXBException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    调用方法

    ZMESSAP003 zmessap003 = new ZMESSAP003();
    zmessap003.setBEDAT("19901101");
    zmessap003.setEDDAT("19901130");
    final XmlRootElement annotation = AnnotationUtil.getAnnotation(ZMESSAP003.class, XmlRootElement.class);
    final String data = buildSOAPRequest(zmessap003, annotation.name());
    final String xml = SOAPClient.send("http://www1.host.com/XISOAPAdapter/MessageServlet?senderParty=&senderService=BS_MES_DEV&receiverParty=&receiverService=&interface=SI_MaterialMasterData_In&interfaceNamespace=http://xxx.com/MES/MaterialMasterData/Sender&sap-user=Visitor&sap-password=qwer1234", data);
    System.out.println(data);
    XPathExpression expression = new XPathExpression();
    expression.setExpression("//ns0:ZMES_SAP_003.Response");
    expression.setNamespaceURI("ns0");
    expression.setTargetNamespace("urn:sap-com:document:sap:rfc:functions");
    final ZMESSAP003Response response = SOAPClient.extractResponse(xml, expression, ZMESSAP003Response.class);

    其中ZMESSAP003 是cxf生成的本地客户端代码,本文不是最终代码,仅供各位参考。

    需要用到的pom依赖

    hutool

    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-xml</artifactId>
    </dependency>
  • 相关阅读:
    数字随机码
    MYSQL数据导出乱码 MYSQL数据导入乱码
    提交表单弹出新窗口
    腾讯捐款居然用Q币,无耻!
    PowerShell如何依靠全局错误处理并执行脚本
    Windows PowerShell 2.0语言之函数和过滤器
    Windows PowerShell 2.0创建调用脚本文件
    Windows PowerShell 2.0 开发之命令别名
    Windows PowerShell 2.0之函数和脚本块共存
    Windows PowerShell 2.0命令别名技巧与内置别名
  • 原文地址:https://www.cnblogs.com/passedbylove/p/15832495.html
Copyright © 2020-2023  润新知