2013-11-09 14 views
7

Sono un servizio che restituisce questo XML:unmarshalling elenco generico con JAXB

<?xml version="1.0" encoding="UTF-8"?> 
<response> 
<status>success</status> 
<result> 
    <project> 
     <id>id1</id> 
      <owner>owner1</owner> 
    </project> 
    <project> 
     <id>id2</id> 
      <owner>owner2</owner> 
    </project> 
</result> 

o

<?xml version="1.0" encoding="UTF-8"?> 
<response> 
<status>success</status> 
<result> 
    <user> 
     <id>id1</id> 
     <name>name1</name> 
    </user> 
    <user> 
     <id>id2</id> 
      <name>name2</name> 
    </user> 
</result> 

voglio unmarshall XML recuperati utilizzando questi classi:

Risultato:

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Response<T> { 

    @XmlElement 
    protected String status; 

    @XmlElementWrapper(name = "result") 
    @XmlElement 
    protected List<T> result; 
} 

Progetto:

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Project { 

    @XmlElement 
    public String id; 

    @XmlElement 
    public String owner; 
} 

utente:

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class User { 

    @XmlElement 
    public String id; 

    @XmlElement 
    public String name; 
} 

Prima soluzione non funziona

JAXBContext context = JAXBContext.newInstance(Response.class, Project.class, User.class); 
Unmarshaller unmarshaller = context.createUnmarshaller(); 

StreamSource source = new StreamSource(new File("responseProject.xml")); 
Response<Project> responseProject = (Response<Project>)unmarshaller.unmarshal(source); 
System.out.println(responseProject.getStatus()); 
for (Project project:responseProject.getResult()) System.out.println(project); 

source = new StreamSource(new File("responseUser.xml")); 
Response<User> responseUser = (Response<User>)unmarshaller.unmarshal(source); 
System.out.println(responseUser.getStatus()); 
for (User user:responseUser.getResult()) System.out.println(user); 

Ottengo una lista vuota.

Seconda soluzione non funziona

Ispirato da questo articolo http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html Ho modificato la classe di risposta:

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Response<T> { 

    @XmlElement 
    protected String status; 

    @XmlAnyElement(lax=true) 
    protected List<T> result; 
} 

E poi provato con questo codice:

Response<Project> responseProject = unmarshal(unmarshaller, Project.class, "responseProject.xml"); 
    System.out.println(responseProject.getStatus()); 
    for (Project project:responseProject.getResult()) System.out.println(project); 

private static <T> Response<T> unmarshal(Unmarshaller unmarshaller, Class<T> clazz, String xmlLocation) throws JAXBException { 
    StreamSource xml = new StreamSource(xmlLocation); 
    @SuppressWarnings("unchecked") 
    Response<T> wrapper = (Response<T>) unmarshaller.unmarshal(xml, Response.class).getValue(); 
    return wrapper; 
} 

E Ottengo questa eccezione leggendo l'elenco delle risposte:

Exception in thread "main" java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to org.test.Project 

Nota: Non riesco a modificare l'XML originale. Ci sono più tipi diversi da Project e User.

+2

il seguirà: http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html –

+0

Ciao Blaise, ho già trovato il tuo articolo, carino, ma ho un eccezione cercando di leggere l'elenco: eccezione nel thread "main" java.lang.ClassCastException: non è possibile eseguire il cast di com.sun.org.apache.xerces.internal.dom.ElementNSImpl su org.test.Project Aggiornare la domanda con la soluzione provata. – Fedy2

+0

È necessario assicurarsi che 'JAXBContext' sia a conoscenza di tutte le classi e che ciascuno degli elementi nella raccolta sia annotato con' @ XmlRootElement'. –

risposta

11

Grazie a Blaise Doughan e al suo articolo ho trovato la soluzione.

In primo luogo abbiamo bisogno della classe wrapper prevista nell'articolo:

@XmlRootElement 
public class Wrapper<T> { 

    private List<T> items; 

    public Wrapper() { 
    items = new ArrayList<T>(); 
    } 

    public Wrapper(List<T> items) { 
    this.items = items; 
    } 

    @XmlAnyElement(lax=true) 
    public List<T> getItems() { 
    return items; 
    } 
} 

Poi ho modificato la classe di reazione al fine di utilizzarlo:

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Response<T> { 

    @XmlElement 
    protected String status; 

    @XmlElement 
    protected Wrapper<T> result; 

    ... 

    public Response(String status, List<T> result) { 
    this.status = status; 
    this.result = new Wrapper<>(result); 
    } 

    ... 

    public List<T> getResult() { 
    return result.getItems(); 
    } 

    ... 
} 

Infine, il codice di deserializzazione:

JAXBContext context = JAXBContext.newInstance(Response.class, Project.class, User.class, Wrapper.class); 
Unmarshaller unmarshaller = context.createUnmarshaller(); 

StreamSource source = new StreamSource(new File("responseProject.xml")); 
Response<Project> responseProject = (Response<Project>)unmarshaller.unmarshal(source); 
System.out.println(responseProject.getStatus()); 
for (Project project:responseProject.getResult()) System.out.println(project); 

source = new StreamSource(new File("responseUser.xml")); 
Response<User> responseUser = (Response<User>)unmarshaller.unmarshal(source); 
System.out.println(responseUser.getStatus()); 
for (User user:responseUser.getResult()) System.out.println(user); 

Ho aggiunto la classe Wrapper all'elenco di classi di contesto.

In alternativa è possibile aggiungere questa annotazione alla classe di risposta:

@XmlSeeAlso({Project.class, User.class}) 
1

Utilizzando @XmlSeeAlso ({Project.class, User.class}) su risposta classi ha l'inconveniente di generare un po 'di spazzatura informazioni su ciascuna entità nella lista: xmlns: xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi: type = "AccountUtente"

<resources> 
    <links> 
     <link> 
      <rel>self</rel> 
      <uri>http://localhost:8080/salonea-1.0/rest/user-accounts?offset=0&amp;limit=2</uri> 
     </link> 
     <link> 
      <rel>prev</rel> 
      <uri></uri> 
     </link> 
     <link> 
      <rel>next</rel> 
      <uri>http://localhost:8080/salonea-1.0/rest/user-accounts?offset=2&amp;limit=2</uri> 
     </link> 
    </links> 
    <collection> 
     <user-account 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="userAccount"> 
      <accountType>user</accountType> 
      <activationCode>638f502a0e409348ccc2e36c24907f0</activationCode> 
      <email>[email protected]</email> 
      <login>michzio</login> 
      <password>sAmPL3#e</password> 
      <registrationDate>2015-09-03T17:30:03+02:00</registrationDate> 
      <userId>1</userId> 
     </user-account> 
     <user-account 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="userAccount"> 
      <accountType>user</accountType> 
      <activationCode>334bc79d142a291894bd71881e38a719</activationCode> 
      <email>[email protected]</email> 
      <login>alicja</login> 
      <password>zAczka!00</password> 
      <registrationDate>2015-09-03T17:30:03+02:00</registrationDate> 
      <userId>2</userId> 
     </user-account> 
    </collection> 
</resources>