【问题标题】:Java WS: How do I send direct XML as part of a SOAP requestJava WS:如何将直接 XML 作为 SOAP 请求的一部分发送
【发布时间】:2013-01-21 00:47:46
【问题描述】:
  1. 我有 SOAP Web 服务的 WSDL

  2. 我在 RAD Developer(与 IBM Websphere 一起使用的基于 Eclipse 的编译器)中创建了一个“自上而下的 Java Bean”Web 服务客户端,并自动生成了一堆 JAX-WS .java 模块

  3. 以下是其中一项操作的自动生成的 JAX-WS 代码:


@WebMethod(operationName = "CommitTransaction", action = "http://myuri.com/wsdl/gitsearchservice/CommitTransaction")

@RequestWrapper(localName = "CommitTransaction", targetNamespace = "http://myuri.com/wsdl/gitsearchservice", className = "com.myuri.shwsclients.CommitTransaction")
@ResponseWrapper(localName = "CommitTransactionResponse", targetNamespace = "http://myuri.com/wsdl/gitsearchservice", className = "com.myuri.shwsclients.CommitTransactionResponse")
public void commitTransaction(
    @WebParam(name = "requestOptions", targetNamespace = "http://myuri.com/wsdl/gitsearchservice")
    RequestOptions requestOptions,
    @WebParam(name = "transactionData", targetNamespace = "http://myuri.com/wsdl/gitsearchservice")
    TransactionData transactionData);

问题:

  • “transactionData”来自大型、复杂的 XML 数据记录。 WSDL 格式与我将在 Java 端编写的 XML 完全匹配,并且与 Web 服务将在服务器端读取的完全匹配。

  • 问:如何绕过“transactionData”参数的 Java 序列化,在我的 SOAP 消息中发送原始 XML?不必读取我的 XML、解析它并逐字段打包 Java“TransactionType”结构?

提前谢谢你!

【问题讨论】:

    标签: java jakarta-ee xml-serialization jax-ws


    【解决方案1】:

    我看到了两种方法:

    1. 很复杂,一旦重新生成任何生成的代码就会崩溃... 深入研究在您生成的服务代理类中创建的 Service、Dispatch 和 BindingProvider 实现——您可以通过替换您自己的 BindingProvider 实现来获得您想要的行为,但您必须进行其他替换才能到达那里。

    2. 通过 XML 序列化,但没有“逐字段打包”的麻烦

    从您所说的“完全匹配”预期格式的原始 XML 字符串开始

    String rawXML = someMethodThatReturnsXml();
    JAXBContext context = JAXBContext.newInstance(TransactionData.class);
    Unmarshaller unmarshaller =  context.createUnmarshaller();
    Object obj = unmarshaller.unmarshal(new StringReader(rawXML));
    TransactionData data = (TransactionData) obj;
    

    如果 jaxb 生成的类“TransactionData”类未注释为“XmlRootElement”,那么您仍然应该能够像这样完成此操作:

    String rawXML = someMethodThatReturnsXml();
    JAXBContext context = JAXBContext.newInstance(TransactionData.class);
    Unmarshaller unmarshaller =  context.createUnmarshaller();
    
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware(true);
    DocumentBuilder db = dbf.newDocumentBuilder();
    InputSource input = new InputSource(new StringReader(rawXML));
    Document doc = db.parse(input);
    JAXBElement<?> element = unmarshaller.unmarshal(doc, TransactionData.class);
    Object obj = element.getValue();
    
    TransactionData data = (TransactionData) obj;
    

    如果您处理大量各种类型的 XML 记录,您可以将它们放在一起,并将所需的输出类作为参数并具有通用的 xml-to-object 实用程序:

    import java.io.IOException;
    import java.io.StringReader;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBElement;
    import javax.xml.bind.JAXBException;
    import javax.xml.bind.Unmarshaller;
    import javax.xml.bind.annotation.XmlRootElement;
    import javax.xml.bind.annotation.XmlType;
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.parsers.ParserConfigurationException;
    import org.w3c.dom.Document;
    import org.xml.sax.InputSource;
    import org.xml.sax.SAXException;
    /**
     * @author <a href="http://stackoverflow.com/users/1904450/zachofalltrades">Zach Shelton</a>
     */
    public class SampleCode {
        /**
         * Turn xml into an object. 
         *
         * @param <SomeJaxbType>
         * @param wellFormedXml a String of well-formed XML, with proper reference to the correct namespace
         * @param jaxbClass the class representing the type of object you want to get back (probably a class generated from xsd or wsdl) 
         * @return an object of the requested type
         * @throws JAXBException if there is a problem setting up the Unmarshaller, or performing the unmarshal operation
         * @throws SAXException if the given class is not annotated as XmlRootElement, and the xml String can not be parsed to a generic DOM Document
         */
        public <SomeJaxbType> SomeJaxbType xmlToObject(String wellFormedXml, Class<SomeJaxbType> jaxbClass) throws JAXBException, SAXException {
            if (jaxbClass==null) throw new IllegalArgumentException("received null jaxbClass");
            if (wellFormedXml==null || wellFormedXml.trim().isEmpty()) throw new IllegalArgumentException("received null or empty xml");
            if (!jaxbClass.isAnnotationPresent(XmlType.class)) throw new IllegalArgumentException(jaxbClass.getName() + " is not annotated as a JAXB class");
    
            JAXBContext context = JAXBContext.newInstance(jaxbClass);
            Unmarshaller unmarshaller =  context.createUnmarshaller();
    
            Object genericObject;
            if (jaxbClass.isAnnotationPresent(XmlRootElement.class)) {
                genericObject = unmarshaller.unmarshal(new StringReader(wellFormedXml));
            } else {//must use alternate method described in API
                    //http://docs.oracle.com/javaee/6/api/javax/xml/bind/Unmarshaller.html#unmarshalByDeclaredType
                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                dbf.setNamespaceAware(true);
                DocumentBuilder db;
                try {
                    db = dbf.newDocumentBuilder();
                } catch (ParserConfigurationException e) {
                    throw new IllegalStateException("failed to get DocumentBuilder from factory");
                }
                InputSource input = new InputSource(new StringReader(wellFormedXml));
                Document doc;
                try {
                    doc = db.parse(input);
                } catch (IOException e) {
                    throw new IllegalStateException("xml string vanished");
                }
                JAXBElement<?> element = unmarshaller.unmarshal(doc, jaxbClass);
                genericObject = element.getValue();
            }
    
            SomeJaxbType typedObject = (SomeJaxbType) genericObject;
            return typedObject;
        }
    }
    

    【讨论】:

    • 有趣。这正是我正在寻找的......如果它有效。我会试试的,让你知道!谢谢!
    • 如果生成的“TransactionData”类没有使用@XmlRootElement 注释,您可能会遇到麻烦——如果是这样,您可以使用另一种方法:docs.oracle.com/javaee/6/api/javax/xml/bind/…
    【解决方案2】:

    我不太熟悉 RequestWrapperResponseWrapper 注释的用法,但是您的出站消息最终看起来像:

    <CommitTransaction>
        <requestOptions>...</requestOptions>
        <transactionData>...</transactionData>
    </CommitTransaction>
    

    让我们假设它确实如此:) 我们还假设TransactionData 参数的XML 由Source 实例表示。创建一个自定义的SOAPHandler 来维护该Source 的句柄:

    public class TransactionDataHandler implements SOAPHandler<SOAPMessageContext> {
        private final Source transactionDataSource;
    
        public TransactionDataHandler(Source transactionDataSource) {
            this.transactionDataSource = transactionDataSource;
        }
    
        @Override
        public boolean handleMessage(SOAPMessageContext context) {
            // no exception handling
            Boolean isOutbound = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
            if (Boolean.TRUE.equals(isOutbound)) {
                SOAPMessage message = context.getMessage();
                SOAPBody body = message.getSOAPBody();
                Node commitTransactionNode = body.getFirstChild();
                Result commitTransactionResult = new DOMResult(commitTransactionNode);
                TransformerFactory.newInstance().newTransformer().transform(this.transactionDataSource, commitTransactionResult);
            }
            return true;
        }
    
        @Override
        public Set<QName> getHeaders() {
            return null;
        }
    
        @Override
        public boolean handleFault(SOAPMessageContext context) {
            return true;
        }
    
        @Override
        public void close(MessageContext context) {
            // no-op
        }
    }
    

    这个想法是转换步骤应该创建子&lt;transactionData&gt; 节点。您还需要一个自定义的HandlerResolver,可能类似于:

    public class TransactionDataHandlerResolver implements HandlerResolver {
        private final Handler transactionDataHandler;
    
        public TransactionDataHandlerResolver(Source transactionDataSource) {
            this.transactionDataHandler = new TransactionDataHandler(transactionDataSource);
        }
    
        @Override
        public List<Handler> getHandlerChain(PortInfo portInfo) {
            return Collections.singletonList(this.transactionDataHandler);
        }
    }
    

    最后,创建一个Service 实例并在XML SourceHandlerResolver 中挂钩:

    Source transactionDataSource;
    URL wsdlDocumentLocation;
    QName serviceName;
    Service service = Service.create(wsdlDocumentLocation, serviceName);
    service.setHandlerResolver(new TransactionDataHandlerResolver(transactionDataSource));
    

    从这里,您可以获得Dispatch 或端口代理并启动操作。这可能不完全适合您现有的代码/环境,但希望它能给您一些思考...

    编辑:如果您使用的是端口代理,请将 null 传递给第二个参数:

    port.commitTransaction(requestOptions, null);
    

    【讨论】:

      猜你喜欢
      • 2014-06-23
      • 1970-01-01
      • 1970-01-01
      • 2015-06-21
      • 2016-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多