Solving unmarshalling problems with Jaxb/CXF
Introduction
This tutorial gives a quick introduction to Jaxb and shows how to fix some common unmarshalling problems with generated CXF web service clients, or other generated clients that make use of the Jaxb api.
It assumes the reader is a Java developer familiar with the basics of classic webservices. He should know a wsdl describes a webservice. He should also know that a webservice client communicates with the webservice by sending a soap request in xml, after which the server (hopefully) responds with a soap response, which is in xml too.
Context
Many Java developers have to make a webservice client from a wsdl from time to time.
Usually, they use a wsdl2java tool to do that, which comes with every major web services framework I know, such as Axis or Cxf, the former Xfire.
One of the things this client will do is, when sending a request, transform – “marshall” – Java objects to a soap xml snippet so they can be included as parameters in the soap request. And when receiving the soap response from the server, the client will have to map the xml snippets in the response back to Java objects – the “unmarshalling” -, so we can easily work with the received response from Java.
To do this marshalling and unmarshalling, the generated code usually makes use of some specific apis.
Cxf, for example, uses Jaxb to marshall java objects to and unmarshall java objects from XML.
What is Jaxb?
Jaxb, the Java Architecture for XML Binding, is part of the jee stack.
Its job is exactly what I just described: to marshal Java objects into XML and to unmarshal XML back into Java objects.
Consider the following class:
package com.integratingstuff.pojo; import java.math.BigDecimal; import java.math.BigInteger; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class Item { private String description; private BigDecimal price; private BigInteger catalogNumber; @XmlAttribute(name = "description") public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @XmlAttribute(name = "price") public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price = price; } @XmlAttribute(name = "catalog-number") public BigInteger getCatalogNumber() { return catalogNumber; } public void setCatalogNumber(BigInteger catalogNumber) { this.catalogNumber = catalogNumber; } }
The Jaxb annotations tell a(n) Jaxb (Un)marshaller how to (un)marshall an Item object/xml snippet.
The @XmlRootElement annotation tells jaxb that the xml equivalent of this class can be the root of an xml document.
The @XmlAttribute annotation tells jaxb to map this property as an xml attribute. If no annotation here wouldnt be specified, the property would still be mapped in the xml, but as a subelement instead of an attribute.
If you dont want the property to appear in the generated xml at all, you have to use @XmlTransient.
Let’s test this and marshall a sample Item:
package com.integratingstuff.jaxb; import java.math.BigDecimal; import java.math.BigInteger; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import com.integratingstuff.pojo.Item; public class DoMarshall { public static void main(String[] args) { try { JAXBContext jaxbContext= JAXBContext.newInstance(Item.class); Item item = new Item(); item.setDescription("Test description"); item.setPrice(new BigDecimal(10)); item.setCatalogNumber(new BigInteger("10")); Marshaller marshaller=jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.marshal(item, System.out); } catch (JAXBException e) { e.printStackTrace(); } } }
Running the above class will render the output:
<?xml version="1.0" encoding="UTF-8"?> <item price="10" description="Test description" catalog-number="10"/>
Obviously, with jaxb it is easy to map Java objects to some xml equivalent.
Common unmarshalling problems
Unmarshalling is just as easy. However, sometimes one bumps into annoying unmarshalling problems.
package com.integratingstuff.jaxb; import java.io.ByteArrayInputStream; import java.io.InputStream; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import com.integratingstuff.pojo.Item; public class DoUnmarshall { public static void main(String[] args) { try { JAXBContext jaxbContext= JAXBContext.newInstance(Item.class); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); String xml = "<?xml version="1.0" encoding="UTF-8"?><item price="" description="Test description" catalog-number="10"/>"; InputStream inputStream = new ByteArrayInputStream(xml.getBytes()); Item item = (Item) unmarshaller.unmarshal(inputStream); } catch (JAXBException e) { e.printStackTrace(); } } }
In the above class, we are trying to unmarshall the xml we got from the previous section back into the java object. Note however, that we made a modification, which will cause the unmarshalling to fail.
We made the price attribute empty instead of passing “10″.
Running this class will result in a “NumberFormatException at java.math.BigDecimal.<init>” exception.
If we would make the catalog-number empty instead, this would result in a “NumberFormatException: Zero length BigInteger at java.math.BigInteger.<init>” exception.
And some webservices will actually return xml like that. Ive already had to work twice with a web service implemented in .NET(the default marshaller in .NET seems to wrap bigintegers and bigdecimals as an empty string?) that returned something like biginteger=”" in the soap response.
Jaxb XmlAdapters
When calling an external webservice, we have no control over what is returned in the soap response. Hence, we will need to fix the problem at the moment we are unmarshalling. Luckily, Jaxb makes it possible to register custom unmarshallers in the form of XmlAdapters.
package com.integratingstuff.jaxb; import java.math.BigDecimal; import javax.xml.bind.annotation.adapters.XmlAdapter; public class BigDecimalXmlAdapter extends XmlAdapter{ @Override public String marshal(BigDecimal bigDecimal) throws Exception { if (bigDecimal != null){ return bigDecimal.toString(); } else { return null; } } @Override public BigDecimal unmarshal(String s) throws Exception { try { return new BigDecimal(s); } catch (NumberFormatException e) { return null; } } }
The above adapter will marshall and unmarshall BigDecimals the standard way, with the exception that if the String has an illegal value at unmarshalling, it will just unmarshall it as null, instead of crashing the whole unmarshalling process. I wrote a similar one for the BigInteger problem: BigIntegerXmlAdapter.
Registering XmlAdapters for a whole package
Although you can register XmlAdapters at a detailed level, per class for example, it is not the easiest method to use them for generated clients. It is likely that you ended up with tons of classes, and you dont want to go annotate all of them seperately.
Luckily, we can just put them in the package declaration file package-info.java of our pojo package:
@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters ({ @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(value=com.integratingstuff.jaxb.BigIntegerXmlAdapter.class,type=java.math.BigInteger.class), @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(value=com.integratingstuff.jaxb.BigDecimalXmlAdapter.class,type=java.math.BigDecimal.class) }) package com.integratingstuff.pojo;
The above code that failed before will now run without problems.
You’re a total stud for posting this info. I couldn’t find it anywhere else, and you saved my butt!!
Was this answer helpful?
LikeDislikeCould not find anything that comes close to explaining the fundamentals.Thanks for this blog and the efforts. Proved to be a life saver for me for the day. Thanks.
Was this answer helpful?
LikeDislikeBrilliant article that has been written clearly. First JAXB article talking about issues while unmarshalling.
Was this answer helpful?
LikeDislikeyou are the man… concepts well explained. you saved my day. Thanks!
Was this answer helpful?
LikeDislikeThis article saved my day as well, thanks.
Was this answer helpful?
LikeDislikeThank you for sharing this. I have an additional question. I have generated a client using a wsdl and wsdl2java. All worked well until the strucure / wsdl was extended by the supplier (n external party). I noticed this when receiving unmarshall exceptions. The external party stated they will update the structure / wsdl reguarly, without notice. The new wsdl will be backwards compatible (ie. only extensions are made).
Is there a way to overcome unmarshall exceptions for new attributes in the return message from the server / ie. skip unknown attributes in the return message?
Best regards,
Bob
Was this answer helpful?
LikeDislikeThe adapter should use generics.
public class BigIntegerXmlAdapter extends XmlAdapter
would be the correct class definition. Great tip though, really helped me. Thx Steffen