I am using a java.xml.bind
-annotated Bean to create an XML output format that omits collections if they are empty. For that purpose the respective Getter has to look like this:
@XmlElementWrapper(name = "titles")
@XmlElement(name = "title")
public List<XmlTitle> getTitles() {
if (titles.size() == 0) {
return null;
}
return titles;
}
This works fine during marshaling. Sadly, I am getting a NullpointerException
once I want to unmarshal the very same XML file:
java.lang.NullPointerException
at com.sun.xml.bind.v2.runtime.reflect.Lister$CollectionLister.addToPack(Lister.java:305)
at com.sun.xml.bind.v2.runtime.reflect.Lister$CollectionLister.addToPack(Lister.java:269)
at com.sun.xml.bind.v2.runtime.unmarshaller.Scope.add(Scope.java:121)
at com.sun.xml.bind.v2.runtime.property.ArrayERProperty$ReceiverImpl.receive(ArrayERProperty.java:213)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.endElement(UnmarshallingContext.java:538)
at com.sun.xml.bind.v2.runtime.unmarshaller.ValidatingUnmarshaller.endElement(ValidatingUnmarshaller.java:107)
at com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector.endElement(SAXConnector.java:158)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:609)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1782)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2973)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:606)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:117)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:258)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:229)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:140)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:123)
at org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:754)
at org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:735)
This has to do with namely that Getter returning null
. As some JAXB expert explains on the Glassfish JAXB forum:
The JAXB unmarshaller does not expect setters for properties of type List; it'll just use x.getSomeList().add( y ) to add another child.
How do I best solve this dilemma?
As per exception JAXB does not allow lists to be null
when marschalling. So instead of using the annotation @XmlElementWrapper(name = "titles")
you could create a wrapper class which you use in the containing class to hold the list of XmnlTitle
. If you look as xjc
generated classes you will find that this is exactly how it handles wrapped list elements.
Also JAXB automatically omits elements which are null
, this is how you managed to not unmarschal the list by returning null
when size() == 0
Wrapper:
public class XmlTitleWrapper {
private List<XmlTitle> title;
public void setTitle(List<XmlTitle> title) {
this.title = title;
}
@XmlElement(name = "title")
public List<XmlTitle> getTitle() {
if(title == null) {
title = new ArrayList<XmlTitle>();
}
return title;
}
@Override
public String toString() {
return "XmlTitleWrapper [title=" + title + "]";
}
}
Container:
@XmlRootElement
public class Container {
private XmlTitleWrapper titles;
@XmlElement(name = "titles")
public XmlTitleWrapper getTitles() {
return titles;
}
public void setTitles(XmlTitleWrapper titles) {
this.titles = titles;
}
@Override
public String toString() {
return "Container [titles=" + titles + "]";
}
}
Test:
Container c1 = new Container();
List<XmlTitle> title = Arrays.asList(new XmlTitle("A"), new XmlTitle("B"));
XmlTitleWrapper wrapper = new XmlTitleWrapper();
wrapper.setTitle(title);
c1.setTitles(wrapper);
StringWriter writer = new StringWriter();
JaxbUtil.toXML(c1, writer);
System.out.printf("%s%n", String.valueOf(writer));
this will generate:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<container>
<titles>
<title>
<value>A</value>
</title>
<title>
<value>B</value>
</title>
</titles>
</container>
If you remove the line setting the wrapper
c1.setTitles(wrapper);
thus leaving it null
in the container, then the output will be:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<container/>
You could create a XmlAdapter in which you control marshalling and unmarshalling behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With