1
  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”结构?

先感谢您!

4

2 回答 2

4

我看到两种方法:

  1. 复杂,并且一旦重新生成任何生成的代码就会崩溃...深入研究在您生成的服务代理类中创建的服务、调度和绑定提供程序实现——您可以通过替换您的自己的 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;
    }
}
于 2013-01-23T17:58:48.347 回答
1

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

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

这个想法是转换步骤应该创建子<transactionData>节点。您还需要一个 custom 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 中创建一个实例Source并挂钩HandlerResolver

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

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

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

port.commitTransaction(requestOptions, null);
于 2013-01-23T19:33:24.923 回答