2012-10-05 13 views
6

Ich versuche, ein XML-Dokument aus einem Legacy-System mit JAXB entpacken. Ich habe eine Struktur xml wie folgt:JAXB-Unmarshalling mit Java Generics

<response> 
    <id>000000</id> 
    <results> 
     <result> 
<!-- Request specific xml content --> 
      <year>2003</year> 
      <title>Lorem Ipsum</title> 
      <items> 
       <item>I1</item> 
       <item>I2</item> 
      </items> 
     </result> 
     <result> 
      <year>2007</year> 
      <title>Dolor sit amet</title> 
      <items> 
       <item>K1</item> 
       <item>K2</item> 
      </items> 
     </result> 
    </results> 
</response> 

Die Tags innerhalb der von <result> Tag angegebenen Teil wird je nach meiner Anfrage ändern. Da der Inhalt ändern kann ich entschieden Generika Produkte für Ergebnis zu verwenden, und ich habe meine Java Beans mit Anmerkungen wie folgt hergestellt:

// imports here 
@XmlRootElement(name="response") 
@XmlAccessorType(XmlAccessType.FIELD) 
public class XResponse<T>{ 
    private String id; 

    @XmlElementWrapper(name="results") 
    @XmlElement(name="result") 
    private List<T> results; 

// setters and getters 
} 

... 

@XmlRootElement(name="result") 
@XmlAccessorType(XmlAccessType.FIELD) 
public class X1Result{ 
    private String year; 
    private String title; 
    @XmlElementWrapper(name="items") 
    @XmlElement(name="item") 
    private List<String> items; 

// setters and getters 
} 
... 

ich das XML-Dokument über den Code versucht unmarshalling unter:

JAXBContext context = JAXBContext.newInstance(XResponse.class, X1Result.class); 
Unmarshaller um = context.createUnmarshaller(); 
XResponse<X1Result> response = (XResponse<X1Result>) um.unmarshal(xmlContent); 

List<X1Result> results = unmarshal.getResults(); 
for (X1Result object : results) { 
    System.out.println(object.getClass()); 
} 

Ich habe ein Problem während der Unmarshalling, dass es die Listenelemente nicht in X1Result Klasse werfen kann. Stattdessen verwendet es org.apache.xerces.dom.ElementNSImpl.

Was soll ich tun, um JAXB Unmarshaller X1Result Klasse zu verwenden?

Vielen Dank im Voraus

Antwort

2

Ich glaube, Sie Vererbung statt Generika verwenden sollten. Gegeben ein XML wie folgt aus:

<?xml version="1.0" encoding="UTF-8"?> 
<response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <id>000000</id> 
    <results> 
     <result xsi:type="X1Result"> 
      <year>2003</year> 
      <title>Lorem Ipsum</title> 
      <items> 
       <item>I1</item> 
       <item>I2</item> 
      </items> 
     </result> 
     <result xsi:type="X1Result"> 
      <year>2007</year> 
      <title>Dolor sit amet</title> 
      <items> 
       <item>K1</item> 
       <item>K2</item> 
      </items> 
     </result> 
    </results> 
</response> 

Sie können Ihre <result> Einträge dynamisch binden. Sie haben ein Top-Level-Typ:

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlType(name = "XResult") 
@XmlSeeAlso({ 
    X1Result.class 
})public abstract class XResult { 

} 

Und Sie haben implementierenden Klassen:

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlType(name = "X1Result") 
public class X1Result extends XResult { 
    @XmlElement(name = "year") 
    private String year; 

    @XmlElement(name = "title") 
    private String title; 

    @XmlElementWrapper(name = "items") 
    @XmlElement(name = "item") 
    private List<String> items; 
    ... 
} 

Verwenden Sie die Top-Level-Typ in Ihrem XResponse Klasse:

@XmlRootElement(name = "response") 
@XmlAccessorType(XmlAccessType.FIELD) 
public class XResponse { 
    @XmlElement(name = "id") 
    private String id; 

    @XmlElementWrapper(name = "results") 
    @XmlElement(name = "result") 
    private List<XResult> results; 
    ... 
} 

Und Sie können entordnen Verwenden des Top-Level-Typs:

context = JAXBContext.newInstance(XResponse.class, XResult.class); 
Unmarshaller unmarshaller = context.createUnmarshaller(); 
XResponse response = (XResponse) unmarshaller.unmarshal(new File("testfile.xml")); 

List<XResult> results = response.getResults(); 
for (XResult object : results) { 
    System.out.println(object.getClass()); 
} 
+0

Der Code über Init ializes 'XResult' statt' X1Result' und daher gibt es Fehler, da es eine abstrakte Klasse ist. Außerdem kommt der Inhalt des XML von einem Legacy-System und es ist keine 'xsi: type'-Information verfügbar. (obwohl ich es programmatisch hinzufügen kann) – Alper

+0

Der Code wird ein 'X1Result' instanziieren, wenn' xsi: type' dort ist. Zumindest macht es das für mich. Viel Glück. – davidfmatheson

+0

Das funktionierte für mich ohne den 'xsi: type', der im XML vorhanden ist. Ich benutze Java 1.7.0_15. –