Back in August, I posted about invoking web services with Spring Integration using XStream marshalling/unmarshalling. That post was based on a project I had just finished at the time. Recently, I had to make some changes to that project in order to implement another web service offered by the same provider. This particular web service was returning a SOAP Map like before, however instead of a simple String key and String value, I had String keys and Map values. With XStream, this seemed to be not so simple to implement. After a bit of looking around, I decided to try and swap out XStream for JAXB. I could have simply added JAXB and used it strictly on the new web service, but it sounded like more fun getting JAXB working across the application with a more sophisticated implementation than what I had done with XStream. The other thing I wanted to do was unmarshal the Map as a List of Objects because for the most part, the key itself didn’t matter too much and there was a finite list of those keys. Here’s a sample excerpt of XML being returned by the new web service.
<item>
<key xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="soapenc:string">mainkey</key>
<value xsi:type="ns1:Map">
<item>
<key xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="soapenc:string">key1</key>
<value xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="soapenc:string">value1</value>
</item>
<item>
<key xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="soapenc:string">key2</key>
<value xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="soapenc:string">value2</value>
</item>
</value>
</item>
Register your classes with the JAXB marshaller
In Spring, there’s the convenient OXM namespace for registering various types of XML marshallers. Here’s how we register the JAXB annotated classes with the JAXB marshaller.
<oxm:jaxb2-marshaller id="jaxbMarshaller">
<oxm:class-to-be-bound name="com.patrickgrimard.ws.Request"/>
<oxm:class-to-be-bound name="com.patrickgrimard.ws.StringResponse"/>
<oxm:class-to-be-bound name="com.patrickgrimard.ws.MapResponse"/>
<oxm:class-to-be-bound name="com.patrickgrimard.util.StringDataItem"/>
<oxm:class-to-be-bound name="com.patrickgrimard.util.ArrayDataItem"/>
</oxm:jaxb2-marshaller>
The first thing I did was start writing an API for dealing with Maps that had String values, and then adapted it to handle Map values.
Unmarshalling simple Maps of String values into a List of Objects
This starts with a simple interface I created using Java Generics called DataItem<T>. Now if anybody’s used JAXB, they know that when unmarshalling XML with JAXB, it can’t handle interfaces, so the objects and their properties need to be concrete implementations that can be instantiated. Here is the DataItem<T> interface.
public interface DataItem<T> {
public String getKey();
public void setKey(String key);
public T getValue();
public void setValue(T value);
}
With this interface, we can specify the type of the Map’s value property. The first implementation of this interface will be StringDataItem. Continue reading
