Usando JAXB per unmarshal / marshal un List

Sto cercando di creare un server REST molto semplice. Ho solo un metodo di test che restituirà un elenco di stringhe. Ecco il codice:

@GET @Path("/test2") public List test2(){ List list=new Vector(); list.add("a"); list.add("b"); return list; } 

Dà il seguente errore:

 SEVERE: un writer del corpo del messaggio per il tipo Java,
 class java.util.Vector e tipo di supporto MIME,
 application / octet-stream, non è stato trovato

Speravo che JAXB avesse un’impostazione predefinita per tipi semplici come String, Integer, ecc. Immagino di no. Ecco cosa ho immaginato:

  a b  

Qual è il modo più semplice per far funzionare questo metodo?

Ho usato l’esempio di @ LiorH e l’ho esteso a:

@XmlRootElement(name="List") public class JaxbList{ protected List list; public JaxbList(){} public JaxbList(List list){ this.list=list; } @XmlElement(name="Item") public List getList(){ return list; } }
@XmlRootElement(name="List") public class JaxbList{ protected List list; public JaxbList(){} public JaxbList(List list){ this.list=list; } @XmlElement(name="Item") public List getList(){ return list; } } 

Nota che usa i generici per poterlo usare con altre classi rispetto a String. Ora, il codice dell’applicazione è semplicemente:

@GET @Path("/test2") public JaxbList test2(){ List list=new Vector(); list.add("a"); list.add("b"); return new JaxbList(list); }
@GET @Path("/test2") public JaxbList test2(){ List list=new Vector(); list.add("a"); list.add("b"); return new JaxbList(list); } 

Perché questa class semplice non esiste nel pacchetto JAXB? Qualcuno vede qualcosa di simile altrove?

 @GET @Path("/test2") public Response test2(){ List list=new Vector(); list.add("a"); list.add("b"); final GenericEntity> entity = new GenericEntity>(list) { }; return Response.ok().entity(entity).build(); } 

Nel caso in cui qualcuno di voi desideri scrivere un wrapper di liste per gli elenchi contenenti elementi di più classi e desidera assegnare un nome XmlElement individuale in base al tipo di Classe senza scrivere le classi X Wrapper, è ansible utilizzare l’annotazione @XmlMixed . In questo modo JAXB assegna un nome agli elementi dell’elenco in base al valore impostato da @XmlRootElement . Quando lo fai devi specificare quali classi potrebbero essere nella lista usando @XmlSeeAlso

Esempio:

Classi possibili nell’elenco

 @XmlRootElement(name="user") public class User {/*...*/} @XmlRootElement(name="entry") public class LogEntry {/*...*/} 

Classe wrapper

 @XmlRootElement(name="records") @XmlSeeAlso({User.class, LogEntry.class}) public static class JaxbList{ protected List records; public JaxbList(){} public JaxbList(List list){ this.records=list; } @XmlMixed public List getRecords(){ return records; } } 

Esempio:

 List l = new List(); l.add(new User("userA")); l.add(new LogEntry(new UserB())); XStream xStream = new XStream(); String result = xStream.toXML(l); 

Risultato:

  ... ...  

In alternativa è ansible specificare i nomi XmlElement direttamente all’interno della class wrapper utilizzando l’annotazione @XmlElementRef

 @XmlRootElement(name="records") @XmlSeeAlso({User.class, LogEntry.class}) public static class JaxbList{ protected List records; public JaxbList(){} public JaxbList(List list){ this.records=list; } @XmlElementRefs({ @XmlElementRef(name="item", type=Object.class), @XmlElementRef(name="user", type=User.class), @XmlElementRef(name="entry", type=LogEntry.class) }) public List getRecords(){ return records; } } 

Questo può essere fatto MOLTO più facile usando la meravigliosa libreria XStream . Nessun involucro, nessuna annotazione.

XML di destinazione

  a b  

serializzazione

(L’alias di String può essere evitato usando il tag di string minuscolo, ma ho usato il codice di OP)

 List  list = new ArrayList (); list.add("a"); list.add("b"); XStream xStream = new XStream(); xStream.alias("Strings", List.class); xStream.alias("String", String.class); String result = xStream.toXML(list); 

deserializzazione

Deserializzazione in ArrayList

 XStream xStream = new XStream(); xStream.alias("Strings", ArrayList.class); xStream.alias("String", String.class); xStream.addImplicitArray(ArrayList.class, "elementData"); List  result = (List )xStream.fromXML(file); 

Deserializzazione in stringa []

 XStream xStream = new XStream(); xStream.alias("Strings", String[].class); xStream.alias("String", String.class); String[] result = (String[])xStream.fromXML(file); 

Si noti che l’istanza XStream è protetta da thread e può essere preconfigurata, riducendo la quantità di codice a one-liner.

XStream può anche essere utilizzato come meccanismo di serializzazione predefinito per il servizio JAX-RS. L’esempio di colbind XStream in Jersey può essere trovato qui

Da un post sul blog personale, non è necessario creare uno specifico JaxbList < T > .

Supponendo un object con un elenco di stringhe:

 @XmlRootElement public class ObjectWithList { private List list; @XmlElementWrapper(name="MyList") @XmlElement public List getList() { return list; } public void setList(List list) { this.list = list; } } 

Un giro di JAXB:

 public static void simpleExample() throws JAXBException { List l = new ArrayList(); l.add("Somewhere"); l.add("This and that"); l.add("Something"); // Object with list ObjectWithList owl = new ObjectWithList(); owl.setList(l); JAXBContext jc = JAXBContext.newInstance(ObjectWithList.class); ObjectWithList retr = marshallUnmarshall(owl, jc); for (String s : retr.getList()) { System.out.println(s); } System.out.println(" "); } 

Produce il seguente:

 < ?xml version="1.0" encoding="UTF-8" standalone="yes"?>   Somewhere This and that Something   

Ho incontrato questo modello alcune volte, ho trovato che il modo più semplice è quello di definire una class interna con annotazioni JaxB. (comunque, probabilmente vorrai definire il nome del tag root)

quindi il tuo codice sarebbe simile a questo

 @GET @Path("/test2") public Object test2(){ MyResourceWrapper wrapper = new MyResourceWrapper(); wrapper .add("a"); wrapper .add("b"); return wrapper ; } @XmlRootElement(name="MyResource") private static class MyResourceWrapper { @XmlElement(name="Item") List list=new ArrayList(); MyResourceWrapper (){} public void add(String s){ list.add(s);} } 

se lavori con javax.rs (jax-rs) restituirei object Response con il wrapper impostato come quadro

Finalmente l’ho risolto usando JacksonJaxbJsonProvider Richiede poche modifiche nel tuo pom.xml spring.xml e Maven pom.xml

Nella tua spring context.xml aggiungi JacksonJaxbJsonProvider a :

      

Nel tuo Maven pom.xml aggiungi:

  org.codehaus.jackson jackson-jaxrs 1.9.0  

L’esempio di User1 ha funzionato bene per me. Ma, come avvertimento, non funzionerà con qualcosa di diverso dai semplici tipi String / Integer, a meno che non si aggiunga un’annotazione @XmlSeeAlso:

 @XmlRootElement(name = "List") @XmlSeeAlso(MovieTicket.class) public class MovieTicketList { protected List list; 

Funziona OK, anche se mi impedisce di utilizzare una singola class di elenchi generici su tutta la mia intera applicazione. Potrebbe anche spiegare perché questa class apparentemente ovvia non esiste nel pacchetto JAXB.

Assicurati di aggiungere il tag @XmlSeeAlso con le tue classi specifiche usate all’interno di JaxbList. È molto importante, inoltre, genera HttpMessageNotWritableException

Avrei risparmiato tempo se avessi trovato prima Resteasy Jackson Provider .

Basta aggiungere il JAR del fornitore Jackson Resteasy . Nessun wrapper di quadro. Nessuna annotazione XML. Nessun corpo del messaggio personalizzato.

Se si sta utilizzando Maven nel progetto jersey, aggiungere sotto pom.xml e aggiornare le dipendenze del progetto in modo che Jaxb sia in grado di rilevare la class del modello e convertire l’elenco in XML del tipo di supporto:

  com.sun.xml.bind jaxb-core 2.2.11  

Per una soluzione più generale, per la serializzazione JAXB-XML di qualsiasi elenco di livello superiore, che richiede solo 1 nuova class da scrivere, controlla la soluzione fornita in questa domanda:

È ansible configurare in modo programmatico JAXB?

 public class Wrapper { private List items = new ArrayList(); @XmlAnyElement(lax=true) public List getItems() { return items; } } //JAXBContext is thread safe and so create it in constructor or //setter or wherever: ... JAXBContext jc = JAXBContext.newInstance(Wrapper.class, clazz); ... public String marshal(List things, Class clazz) { //configure JAXB and marshaller Marshaller m = jc.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); //Create wrapper based on generic list of objects Wrapper wrapper = new Wrapper(things); JAXBElement wrapperJAXBElement = new JAXBElement(new QName(clazz.getSimpleName().toLowerCase()+"s"), Wrapper.class, wrapper); StringWriter result = new StringWriter(); //marshal! m.marshal(wrapperJAXBElement, result); return result.toString(); }