2009-06-23 17 views
14

supponiamo di avere questa classe:collezioni unmarshalling in JAXB

public class A { 

    private HashMap<String, B> map; 

    @XmlElement 
    private void setB(ArrayList<B> col) { 
     ... 
    } 

    private ArrayList<B> getB() { 
     ... 
    } 

} 

Quando si cerca di unmarshall un documento XML a questa classe utilizzando JAXB mi accorgo che invece di chiamare il metodo SETB() e mi invio la lista dei B istanze JaxB chiama effettivamente getB() e aggiunge le istanze B all'elenco restituito. Perché?

Il motivo per cui il setter deve essere chiamato è che la lista è in realtà solo una memoria temporanea da cui voglio costruire il campo mappa, quindi ho pensato di farlo nel setter.

Grazie.

risposta

8

questo è il modo in cui jaxb gestisce le raccolte. devi essere sicuro di avere una raccolta non nullo quando jaxb prova a diventare unmarshal.

v'è un plugin (mai usato da solo), ma può essere utile: https://jaxb2-commons.dev.java.net/collection-setter-injector/

+0

+1 per indicare il modo in cui JAXB fa collezioni, ma quel plugin non aiuterà. Questo è un plugin per il codice XJC generatror, ​​me non cambia il comportamento di runtime di JAXB. – skaffman

+0

Ho letto alcuni vecchi thread di posta elettronica che affermavano che questo comportamento veniva corretto in JaxB 2.1. Mi aspetto che ci sia una proprietà su JaxbContext.newInstance (classes, * properties *) che controlla il comportamento di marshalling, ma non riesco a trovarla. – Justin

3

JAXB ha problemi di supporto interfacce e classi astratte; di solito non sa quale sottoclasse istanziare. Il problema è, si tratta di un modello comune per avere una classe lungo le linee di:

ArrayList list; 

@XMLElement 
public List getList() {return this.list;} 

Per aggirare il problema, JAXB non ha nemmeno tentare di creare un'istanza della classe di proprietà (ad esempio, List) derivato dal getter/setter coppia se si tratta di una collezione. Presume solo che non è nullo e modificabile.

Probabilmente il lavoro più semplice consiste nel contrassegnare l'interfaccia aziendale con @XMLTransient e aggiungere una coppia getter/setter diversa con @XMLElement per la visualizzazione dei dati che si desidera esporre a JAXB. Di solito li faccio protetti piuttosto che pubblici, perché non mi interessa avere il comportamento un po 'stupido di JAXB come parte del contratto pubblico delle mie classi.

6

Hy,

è possibile utilizzarlo con JAXB, è un lavoro !!! (Con Maven ....)

<plugin> 
      <groupId>org.jvnet.jaxb2.maven2</groupId> 
      <artifactId>maven-jaxb2-plugin</artifactId> 
      <executions> 
       <execution> 
        <goals> 
         <goal>generate</goal> 
        </goals> 
       </execution> 
      </executions> 
      <configuration> 
       <args> 
        <arg>-Xcollection-setter-injector</arg> 
       </args> 
       <plugins> 
        <plugin> 
         <groupId>net.java.dev.vcc.thirdparty</groupId> 
         <artifactId>collection-setter-injector</artifactId> 
         <version>0.5.0-1</version> 
        </plugin> 
       </plugins> 
       <schemaDirectory>src/schemas</schemaDirectory> 
       <generateDirectory>src/main/java</generateDirectory> 
       <extension>true</extension> 
      </configuration> 
     </plugin> 

e si ottiene il setter per la vostra collezione

Spero che avrebbe aiutato le persone

bye

+0

scegliere il pacchetto, è possibile anche aggiungere il parametro com.your.package George

1

Jaxb2 unmarshaller definisce un'interfaccia ascoltatore che si chiama ogni volta che un oggetto è stato smistato. È possibile definire un listener personalizzato per richiamare i metodi setter su tutte le raccolte (o su oggetti secondari). Questo dovrebbe essere abbastanza facile da fare con una qualsiasi delle classi di bean utils. Sto cercando un'implementazione esistente, anche se non la vedo.

JAXBContext context = JAXBContext.newInstance(classesToBeBound); 
m_unmarshaller = context.createUnmarshaller(); 
m_unmarshaller.setListener(
    new Unmarshaller.Listener() { 
    public void afterUnmarshal(Object target, Object parent) { 
    for (Property p : getBeanProperties(target.getClass())) 
     if (p.isCollectionType() || p.isCompositeType()) 
     p.invokeSetter(p.invokeGetter()); 
    } 
    }); 

Se si utilizza il quadro di primavera, la sua abbastanza semplice:

new Unmarshaller.Listener() { 
     public void afterUnmarshal(Object target, Object parent) { 
      BeanWrapper wrapper = new BeanWrapperImpl(target); 
      for (PropertyDescriptor pd : wrapper.getPropertyDescriptors()) { 
       if (pd.getPropertyType() != null) { 
         if (!BeanUtils.isSimpleProperty(pd.getPropertyType())) { 
          try { 
           Method setter = pd.getWriteMethod(); 
           if (setter != null) { 
            Method getter = pd.getReadMethod(); 
            if (getter != null) 
             setter.invoke(target, getter.invoke(target)); 
           } 
          } 
          catch (Exception ex) { 
           s_logger.error("can't invoke setter", ex); 
          } 
         } 
       } 
      } 
     } 
    } 
1

Si può semplicemente utilizzare un array invece di List)

5

Nota: Sono in vantaggio EclipseLink JAXB (MOXy) e un membro del gruppo di esperti JAXB 2 (JSR-222).

Il comportamento visualizzato varia a seconda delle implementazioni JAXB. Se non si inizializza un valore per la proprietà List, EclipseLink JAXB (MOXy) chiamerà il metodo impostato come previsto.

Per ulteriori informazioni


ESEMPIO

Un

package forum1032152; 

import java.util.ArrayList; 

import javax.xml.bind.annotation.XmlElement; 
import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement 
public class A { 

    private ArrayList<B> b; 

    @XmlElement 
    public void setB(ArrayList<B> col) { 
     System.out.println("Called setB"); 
     for(B b : col) { 
      System.out.println(b); 
     } 
     this.b = col; 
    } 

    public ArrayList<B> getB() { 
     return b; 
    } 

} 

B

package forum1032152; 

public class B { 

} 

Demo

package forum1032152; 

import java.io.File; 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     JAXBContext jc = JAXBContext.newInstance(A.class); 

     File xml = new File("src/forum1032152/input.xml"); 
     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     unmarshaller.unmarshal(xml); 
    } 

} 

input.xml

<?xml version="1.0" encoding="UTF-8"?> 
<a> 
    <b></b> 
    <b></b> 
</a> 

uscita

Called setB 
[email protected] 
[email protected] 
0
The reason I want the setter to be called is that the list is actually 
just a temporary storage from which I want to build the map field, 
so I thought to do it in the setter. 

JAXB può gestire direttamente mappe, quindi, questo potrebbe effettuare la chiamata di SETB() un punto controverso. Se questa è una soluzione accettabile per te, consulta lo example che mantengo sul mio blog per creare un adattatore per le mappe in JAXB.

Problemi correlati