2010-11-15 16 views
9

Una classe è definita con la seguente annotazione JAXB:Trasformare elemento vuoto in nulla quando unmarshalling con JAXB

class Course { 
@XmlElement (name = "book") 
List<Book> requiredBooks = new ArrayList<Book>(); 

Quando unmarshalling un documento XML che contiene questa

<course> 
    ... 
    <book/> 
</course> 

io alla fine con un libro aggiunto alla lista, con tutti i suoi attributi impostati su null. Non controllo l'input XML. Come posso evitare che questo libro vuoto venga aggiunto? Ho provato a intercettare in set ..() o aggiungere ..() metodi, ma risulta che JAXB ignora i setter quando si tratta di collezioni. Eventuali suggerimenti?

risposta

5

Ecco una soluzione che funziona . Ancora una volta, l'eleganza è stata lasciata alle spalle.

Esistono due callback definiti da JAXB: beforeUnmarshal e afterUnmarshal. Implementando afterUnmarshal per pulire l'elenco, sono riuscito a ottenere il risultato desiderato.

class Course 
    { 
    @XmlElement (name = "book") 
    List<Book> requiredBooks = new ArrayList<Book>(); 
    ... 
    void afterUnmarshal(Unmarshaller aUnmarshaller, Object aParent) 
    { 
     if (requiredBooks != null) 
     { 
      Iterator<Book> iterator = requiredBooks.iterator(); 
      while (iterator.hasNext()) 
      { 
       Book book = iterator.next(); 
       if (StringUtils.isEmpty(book.getTitle())) 
       { 
        // a book without title is considered invalid 
        iterator.remove(); 
       } 
      } 
     } 
    } 
    } 

Mentre questo funziona, dal più grande problema con esso è l'assenza di un'interfaccia per l'attuazione afterUnmarshal. Viene analizzato da JAXB usando la reflection, ma penso che sarebbe stato conveniente (e avrebbe ridotto il debugging/la manutenzione) se JAXB avesse semplicemente fornito interfacce e/o implementazioni di absract. Così com'è, cosa succede se la firma di afterUnmarshal cambia in futuro? Questo codice smetterà misteriosamente di funzionare come dovrebbe.

+0

Sto avendo lo stesso problema. Brutta soluzione ma almeno funziona. Nessuna possibilità hai trovato una soluzione migliore? –

1

Prova questo,

class Course { 
@XmlElement (name="book", required=false) 
List<Book> requiredBooks = new ArrayList<Book>(); 
+0

Ciao. Grazie, ma non ha funzionato. Immagino che questa direttiva indichi a JAXB di non considerarlo un errore se manca completamente l'elemento, ma non altera il comportamento quando si verifica un'occorrenza di un tag vuoto. – torngat

+0

@torngat: hai provato 'required' con' nillable'? In realtà, sto indovinando alla cieca, perché non posso credere che non abbia alcuna soluzione integrata per questo requisito ragionevole usuale di base. –

+0

Sì, l'ho provato e nessun risultato. La mia comprensione a questo punto è che quelle istruzioni extra sono usate principalmente per il marshalling (cioè la generazione) dell'XML. Il marshaller dovrebbe quindi sapere che un elemento non è richiesto ma, se fosse nullo, sarebbe incluso in questo modo: torngat

2

Beh, ho trovato un modo per farlo, anche se non trovo super elegante.

Edit: In realtà, la soluzione qui di seguito non lavoro correttamente. In realtà non vengono aggiunte voci all'elenco, indipendentemente dal fatto che siano un elemento vuoto o completamente formato con i dati.

Forse ho sbagliato, quindi qualsiasi commento è benvenuto.

ho creato un adattatore per gestire la conversione e informato JAXB con un'annotazione:

class Course { 
@XmlElement (name = "book") 
@XmlJavaTypeAdapter(BookListAdapter.class) 
List<Book> requiredBooks = new ArrayList<Book>(); 

e quindi definito il mio adattatore:

static class BookListAdapter extends XmlAdapter<Book[], List<Book>> 
{ 
    public Book[] marshal(List<Book> aBookList) 
    { 
     if (aBookList!= null) 
     { 
      return aBookList.toArray(new Book[aBookList.size()]); 
     } 
     return null; 
    } 

    public List<Book> unmarshal(Book[] aBookArray) 
    { 
     List<Book> bookList = new ArrayList<Book>(); 
     if (aBookArray != null) 
     { 
      for (Book book : aBookArray) 
      { 
       if (StringUtils.isNotEmpty(book.getTitle())) 
       { 
        bookList.add(book); 
       } 
      } 
     } 
     return bookList; 
    } 
} 

Funziona come lo voglio, ma, come ho detto prima, non sono convinto della sua eleganza.
Modifica: Come descritto sopra, questo non funziona ma probabilmente a causa di un errore da parte mia.

4

In EclipseLink JAXB (MOXy) abbiamo il concetto di una politica nulla. Questa politica nulla ti dà una certa flessibilità su come rappresentare null.

Si potrebbe modificare la propria classe per simile al seguente:

import org.eclipse.persistence.oxm.annotations.XmlMarshalNullRepresentation; 
import org.eclipse.persistence.oxm.annotations.XmlNullPolicy; 

@XmlRootElement 
class Course { 

    @XmlElement (name = "book") 
    @XmlNullPolicy(
     nullRepresentationForXml = XmlMarshalNullRepresentation.EMPTY_NODE, 
     emptyNodeRepresentsNull=true) 
    List<Book> requiredBooks = new ArrayList<Book>(); 
} 

Questo informa moxy JAXB che per questa proprietà che si desidera trattare elementi vuoti come null per questa proprietà.

Per questo XML:

<course> 
    <book/> 
    <book> 
     <title>Hello World</title> 
    </book> 
    <book/> 
</course> 

Il requiredBooks proprietà sarebbe Unmarshal come: null, aBook, null.

Attualmente c'è un bug che impedisce a questo di funzionare che stiamo affrontando ora. È possibile seguire l'avanzamento su questo tema qui:

+0

Sì, ero a conoscenza di questa possibilità per MOXy (un altro thread ne parlava). Posso solo desiderare che fosse disponibile in JAXB per impostazione predefinita. Sembra strano che una tale opzione sia stata lasciata fuori. – torngat

1

ho fatto un cludge simile:

public List<Photo> getPhotos() { 
    removeUninitializedPhotos(); 
    return photos; 
} 

private void removeUninitializedPhotos() { 
    List<Photo> photosToRemove = new ArrayList<Photo>(); 
    for (Photo photo : photos) { 
     if (photo.getId() == null) { 
      photosToRemove.add(photo); 
     } 
    } 
    photos.removeAll(photosToRemove); 
} 

ma ho trovato così orribile che ho appena finito per passare a Gson ;)

0

Ho avuto un problema simile - Ho bisogno di gestire un ArrayList contenente interfacce. Ho appena creato un adattatore per l'interfaccia (MyInterfaceAdapter) e annotato l'elenco:

@XmlElementWrapper(name="myInterface") 
@XmlJavaTypeAdapter(value=MyInterfaceAdapter.class) 
List<MyInterface> list = new ArryList<MyInterface>; 

mio adattatore simile a questa:

public class MyInterfaceAdapter extends XmlAdapter<MyType, MyInterface> { 

@Override 
public Sensor unmarshal(MyType v) throws Exception { 
    return (MyInterface) v; 
} 

@Override 
public AbstractSensor marshal(MyInterface v) throws Exception {  
     if(v == null) //In the case of empty list 
     { 
     return null; 
     } else 
     { .....return new ... } 
    } 
} 

Dopo unmarshalling Ho un ArrayList inizializzata (anche vuota) .

Penso che nel problema sopra riportato si possano aggiungere condizioni aggiuntive e restituire null nel caso di un libro vuoto. D'altra parte non mi piace nemmeno restituire null.

Problemi correlati