2013-01-21 109 views
1
  1. 我有一个SOAP Web服务Java的WS:我如何直接发送XML作为SOAP请求

  2. 我创建了一个WSDL中的一部分,“自上而下,Java Bean的” Web服务客户端在RAD开发人员(使用IBM WebSphere使用一个基于Eclipse的编译器)和自动生成的一束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

我看到两种方法:

  1. 复杂,并会尽快为任何生成的代码重新genereated土崩瓦解...... 深入到了在创建的服务,调度,和BindingProvider实现您生成的服务代理类 - 您可以通过替换您自己的BindingProvider实现来获得所需的行为,但必须进行其他替换才能达到此目的。

  2. 经过XML serializtion,但没有

“通过现场打包场”开始你的原始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到对象的实用程序:

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; 
    } 
} 
+0

有趣。它*完全*我在找什么...如果它有效。我会尝试并让你知道!谢谢! – paulsm4

+0

如果生成的“TransactionData”类未使用@XmlRootElement注释,则可能会遇到麻烦 - 如果是这样,则可以使用其他方法:http://docs.oracle.com/javaee/6/api/javax/xml /bind/Unmarshaller.html#unmarshalByDeclaredType – ZachOfAllTrades

1

我不是那个熟悉RequestWrapperResponseWrapper注释的用法,但确实出站消息最终看起来类似:

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

让我们假设它确实:),我们还假设了XML的TransactionData param由代表实例。创建一个自定义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>节点。您还需要自定义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); 
    } 
} 

最后,在XML SourceHandlerResolver创建一个Service实例和钩:

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

从这里,你可以得到一个Dispatch或端口代理并启动操作。这可能不是恰好适合你现有的代码/环境,但希望它给你一些精神食粮......

编辑:如果您使用的是端口的代理,通过null第二ARG:

port.commitTransaction(requestOptions, null);