2013-01-21 53 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);