JAXB编组/解组NullpointerException困境

时间:2015-07-29 10:28:20

标签: java xml jaxb

我正在使用java.xml.bind - 带注释的Bean来创建XML输出格式,忽略集合如果它们为空。为此目的,相应的Getter必须如下所示:

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

这在编组期间工作正常。遗憾的是,一旦我想解组相同的XML文件,我就得到了NullpointerException

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)

这与Getter返回null有关。正如一些JAXB专家在Glassfish JAXB forum上解释的那样:

  

JAXB unmarshaller不期望类型属性的setter   清单;它只是使用x.getSomeList()。add(y)来添加另一个孩子。

我如何才能最好地解决这个难题?

3 个答案:

答案 0 :(得分:1)

根据例外,当marschalling时,JAXB不允许列表为null。因此,您可以创建一个包含类,而不是使用注释@XmlElementWrapper(name = "titles"),您可以在包含的类中使用它来保存XmnlTitle列表。如果你看起来像xjc生成的类,你会发现这正是它处理包装列表元素的方式。

此外,JAXB会自动省略null元素,这是您在null

时返回size() == 0而无法解除列表取消联系的方法

打包机:

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 + "]";
    }
}

容器:

@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 + "]";
    }
}

测试:

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

这会产生:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<container>
    <titles>
        <title>
            <value>A</value>
        </title>
        <title>
            <value>B</value>
        </title>
    </titles>
</container>

如果删除设置包装器的行

c1.setTitles(wrapper);

因此将null留在容器中,然后输出为:

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

答案 1 :(得分:1)

您可以创建一个XmlAdapter,您可以在其中控制编组和解组行为。

答案 2 :(得分:0)

您可以通过实现JAXB定义的回调来解决它。使用unmarshaller回调,afterUnmarshal来清理列表。

@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();
            }
        }
    }
}