JAXB Sort / Disassemble Nullpointer Exception Exception

I use java.xml.bind -annotated Bean to create an XML output format that skips collections if they are empty . To do this, the corresponding Getter should look like this:

 @XmlElementWrapper(name = "titles") @XmlElement(name = "title") public List<XmlTitle> getTitles() { if (titles.size() == 0) { return null; } return titles; } 

This works well during marshaling. Unfortunately, I get a NullpointerException when I want to decouple the 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 is because Getter returns null . As a JAXB expert on the Glassfish JAXB forum explains:

Unmarshaller JAXB does not expect setters for properties of type List; it will just use x.getSomeList (). add (y) to add another child.

What is the best way to solve this dilemma?

+5
source share
3 answers

As an exception, JAXB does not allow null lists when marching. So instead of using the @XmlElementWrapper(name = "titles") annotation, you can create a wrapper class that you use in the containing class to save the XmnlTitle list. If you look like xjc generated classes, you will find that this is exactly how it handles wrapped list items.

JAXB also automatically skips items that are null , so you were able to not display the list by returning null when size() == 0

Packing:

 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); 

leaving it null in the container, then the output will be:

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <container/> 
+1
source

You can create an XmlAdapter in which you control the behavior of marshalling and unmarshalling.

+1
source

You can solve this problem by doing callbacks defined by JAXB. Use the unmarshaller, afterUnmarshal callback to clear the list.

 @XmlElement (name = "title") List<XmlTitle> theTitles = new ArrayList<XmlTitle>(); ... void afterUnmarshal(Unmarshaller aUnmarshaller, Object aParent) { if (requiredBooks != null) { Iterator<XmlTitle> iterator = theTitles.iterator(); while (iterator.hasNext()) { XmlTitle theTitle = iterator.next(); if (null == theTitle) { iterator.remove(); } } } } 
0
source

All Articles