2015-07-29 4 views
5

Używam java.xml.bind -annotated Bean stworzyć format wyjściowy XML pomija kolekcje jeśli one są puste. W tym celu odpowiedni Getter musi wyglądać następująco:JAXB marshalling/unmarshalling NullPointerException dylemat

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

Działa to dobrze podczas marshallingu. Niestety, jestem coraz NullpointerException raz chcę unmarshal tego samego pliku XML:

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) 

ten ma do czynienia z mianowicie Getter powrocie null. Jak niektórzy wyjaśnia ekspert JAXB na Glassfish JAXB forum:

JAXB unmarshaller nie spodziewa ustawiające dla właściwości typu listy; użyje po prostu x.getSomeList(). add (y), aby dodać kolejne dziecko.

Jak najlepiej rozwiązać ten dylemat?

+0

Zachowaj logikę poza komponentem bean? –

+1

Dlaczego zwraca 'null', a nie pustą listę? –

+0

@BuhakeSindi Jeśli zwrócę pustą listę, kończę z niechcianymi pustymi tagami w wyjściu XML. –

Odpowiedz

0

Możesz go rozwiązać, realizując funkcje zwrotne zdefiniowane przez JAXB. Użyj wywołania zwrotnego unmarshaller, po Unnmarshal, aby wyczyścić 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(); 
      } 
     } 
    } 
} 
1

Jak za wyjątkiem JAXB nie pozwala wykazy być null gdy marschalling. Dlatego zamiast używać adnotacji @XmlElementWrapper(name = "titles") można utworzyć klasę opakowania, której używa się w klasie zawierającej do przechowywania listy XmnlTitle. Jeśli wyglądasz jak wygenerowane klasy xjc, przekonasz się, że jest to dokładnie sposób, w jaki obsługuje on zawijane elementy listy.

także JAXB automatycznie pomija elementy, które są null, to w jaki sposób udało ci się nie unmarschal listę wracając null gdy 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 + "]"; 
    } 
} 

opakowaniu:

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

ten wygeneruje:

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

Jeśli usuniesz ustawienie owijki

c1.setTitles(wrapper); 

więc pozostawienie go null w pojemniku z linii, to wyjście będzie:

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

Możesz utworzyć XmlAdapter, w którym będziesz kontrolować zestawianie i unmarshalli Zachowanie.