Tuesday, March 03, 2009

DotNet Web Services From Axis2

I had to connect recently to some DotNet Web Services that were returning node names starting with an upper case (as a difference with Java where nodes start with lower case as Java class members do). I already posted a project about this but just wanted to conclude here.

This situation generates a problem at least forthe latest Axis2-1.4.1 which will be unable to dynamically deserialize the response (Assumming BeanUtil#deserialize() is used here). I have described this problem (http://markmail.org/message/2d2r7qlwnoeqka5m) in the mailing list (org.apache.ws.axis-dev).

For unmarshalling or deserializing XML into Java objects there are several Object-to-XML mapping (OXM) libraries: JAXB, JiBX, XMLBeans, Castor, XStream. Most of them are supported by Spring Web Services (Spring-WS) which provides a full stack to build and consume web services.

Another solution is going low level and use SAX (Simple API for XML), StAX (the streaming API for XML).

I have successfully worked around this issue using JAXB. The only reason that stopped me from using Sprint-WS was to stick to the old implementation code for the client (written using ADB stubs)

Using JAXB
1. Download JAXB and follow instructions from the site.
2. From the JAXB lib folder generate the classes from schema like in:
java -jar jaxb-xjc.jar -p com.nestorurquiza.axis2.samople.client.jaxb "https://mydomain/some/path/serviceShema.xsd" -d /path/to/project/client/jaxb/
3. You end up with generated classes that can be used from the client code to unmarshal the SOAP service response into Java objects. Below is a snippet as to how to get it working starting from an OMElement (Refer to my previous tutorial about Axis2 Dynamic Web Services). We are marshalling 'SampleObject' below. Note that SampleObject' is actually a class generated from the schema as explained before:
import org.apache.axiom.om.OMElement;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import java.io.Reader;
import java.io.StringReader;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import com.nestorurquiza.axis2.sample.client.jaxb.SimpleObject;

//AXIOM stuff ...
OMElement result = sender.sendReceive(sampleObjectPayload);
OMElement firstElement = result.getFirstElement();
//The data element is a child of the first element for this example
OMElement dataElement = firstElement.getFirstElement();

//JAXB stuff ... Contextualize JAXB within the package holding the generated from schema sources
JAXBContext jc = JAXBContext.newInstance("com.nestorurquiza.axis2.sample.client.jaxb");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Reader reader = new StringReader(dataElement.toString());
Source source = new StreamSource(reader);
//JAXB always unmarshals to a JAXBElement. Notice the below will work even if the XML node has a different name than the class.
//If the node and the class are named the same then just use SampleObject sampleObject = unmarshaller.unmarshal(source); instead.
JAXBElement root = unmarshaller.unmarshal(source, SampleObject.class);
SampleObject sampleObject = root.getValue();
//Let us retrieve one of the members from our 'SampleObject'. Note the use of getValue().
String sampleObjectMember = sampleObject.getSampleObjectMember().getValue();
//We can use our String member now
System.out.println("sampleObjectMember: " + sampleObjectMember);


Again, should the current Axis2 Axiom API work with node names starting with upper case we should have avoid all of the above JAXB code and just use:

SampleObject sampleObject = (SampleObject) BeanUtil.deserialize(SampleObject.class, dataElement, new DefaultObjectSupplier(), "SampleObject");
Here are the dependencies I successfully used:

1 comment:

Charles said...

Thanks for sharing, I will bookmark and be back again
software engineering services