I've got the WSDL for a SOAP web service
I created a "Top down, Java Bean" web service client in RAD Developer (an Eclipse based compiler used with IBM Websphere) and auto-generated a bunch of JAX-WS .java modules
Here is the auto-generated JAX-WS code for one of the operations:
@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);
QUESTION:
"transactionData" comes from a large, complex XML data record. The WSDL format exactly matches the XML I'll be writing on the Java side, and exactly matches what the Web service will be reading on the server side.
Q: How do I bypass Java serialization for the "transactionData" parameter, to send raw XML in my SOAP message? Instead of having to read my XML, parse it, and pack the Java "TransactionType" structure field-by-field?
Thank you in advance!
I see two approaches:
complicated, and will fall apart as soon as any generated code is re-genereated... Dig into the Service, Dispatch, and BindingProvider implementations that are created in your generated service Proxy class -- you can get the behavior you want by substituting your own BindingProvider implementation, but you have to make other substitutions to get there.
go through XML serializtion, but without the hassle of "packing field by field"
starting with your string of raw XML that you say "exactly matches" the expected format
String rawXML = someMethodThatReturnsXml();
JAXBContext context = JAXBContext.newInstance(TransactionData.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Object obj = unmarshaller.unmarshal(new StringReader(rawXML));
TransactionData data = (TransactionData) obj;
if the jaxb generated class 'TransactionData' class is not annotated as 'XmlRootElement' then you should still be able to accomplish this like so:
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;
If you deal with a lot of XML records of various types, you can throw these together, and make the desired output class a parameter and have a generic xml-to-object utility:
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;
}
}