2011-10-21 18 views
8

Questo è il mio scenario. Ho una classe generica:Campo duplicato in XML generato utilizzando JAXB

public class Tuple<T> extends ArrayList<T> { 
    //... 
    public Tuple(T ...members) { 
    this(Arrays.asList(members)); 
    } 

    @XmlElementWrapper(name = "tuple") 
    @XmlElement(name = "value") 
    public List<T> getList() { 
    return this; 
    } 
} 

E una classe figlia:

public class StringTuple extends Tuple<String> { 
    public StringTuple(String ...members) { 
    super(members); 
    } 

    //explanation of why overriding this method soon ... 
    @XmlElementWrapper(name = "tuple") 
    @XmlElement(name = "value") 
    @Override 
    public List<String> getList() { 
    return this; 
    } 
} 

Queste classi si fa riferimento qui:

@XmlRootElement(namespace = "iv4e.xml.jaxb.model") 
public class Relation { 
    private Tuple<StringTuple> relationVars; 
    //... 
    @XmlElementWrapper(name = "allRelationVars") 
    @XmlElement(name = "relationVarsList") 
    public Tuple<StringTuple> getRelationVars() { 
    return relationVars; 
    } 
} 

Poi un oggetto relazione viene creato con qualcosa di simile:

Relation rel = new Relation(); 
rel.setRelationVars(new Tuple<StringTuple>(
    new StringTuple("RelationshipVar1"), new StringTuple("RelationshipVar2"))); 

Dopo il marshalling questo oggetto, l'output XML è la seguente:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<ns2:relation xmlns:ns2="iv4e.xml.jaxb.model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=""> 

    <allRelationVars> 
    <relationVarsList> 
     <tuple> 
      <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">RelationshipVar1</value> 
     </tuple> 
     <tuple> 
      <value>RelationshipVar1</value> 
     </tuple> 
    </relationVarsList> 
    <relationVarsList> 
     <tuple> 
      <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">RelationshipVar2</value> 
     </tuple> 
     <tuple> 
      <value>RelationshipVar2</value> 
     </tuple> 
    </relationVarsList> 
    </allRelationVars> 

</ns2:relation> 

Così i value elementi sono duplicati !.

Ora, il motivo per lo StringTuple classe sostituisce List<T> getList() con List<String> getList() è di evitare i fastidiosi generati xmlns:xs attributi in ogni membro della lista (gli value elementi del documento XML). Ma poi ogni membro della lista viene mostrato due volte nell'output. Apparentemente, è perché sia ​​il metodo genitore sovrascritto che il metodo figlio sono annotati con @XmlElement. Quindi la mia domanda principale è: c'è un modo per ignorare i metodi sovrascritti annotati con @XmlElement in Jaxb? (considerando che il metodo di overrideing è anche annotato con @XmlElement)

Ho trovato un vecchio post che riportava un problema simile: http://old.nabble.com/@XmlElement-on-overridden-methods-td19101616.html, ma non ho ancora trovato alcuna soluzione. Si noti inoltre che l'aggiunta di un'annotazione @XmlTransient al metodo getList nella classe genitore (Tuple<T>) potrebbe risolvere questo problema, ma ne genererà altri, poiché la classe padre non è astratta e viene utilizzata da sola in altri contesti.

Una domanda secondaria a lato: è possibile dichiarare l'attributo xmlns:xs al nodo radice invece di farlo - apparentemente - che appare in ogni nodo in cui è necessario? So che questo può essere fatto con la classe NamespacePrefixMapper, ma poiché è una classe interna non standard, SUN, preferisco piuttosto usare un approccio più indipendente dall'implementazione.

Grazie in anticipo per qualsiasi feedback!

+1

Prima di arrivare troppo lontano in questo problema, non gli oggetti del dominio hanno bisogno di estendere 'ArrayList'? Perché 'Tuple' estende 'ArrayList' invece di avere una proprietà di tipo' ArrayList'? –

+0

Ciao Blaise !, ho scelto di ereditare da ArrayList poiché il mio oggetto Tuple è in realtà un ArrayList. Tuttavia, potrei conviverci con un ArrayList, anche se poi dovrei implementare alcuni metodi di delega. Ad ogni modo, questo non risolverà il mio problema poiché avrei ancora una classe generica Tuple e un StringTuple che estende la Tupla . – Sergio

+0

Ho aggiunto una risposta con un approccio che potrebbe essere utilizzato per sbarazzarsi della dichiarazione 'xsi: type'. –

risposta

3

è possibile utilizzare il seguente approccio della marcatura della proprietà @XmlTransient sul genitore e @XmlElement sul bambino:

Parent

package forum7851052; 

import java.util.ArrayList; 
import java.util.List; 

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

@XmlRootElement 
public class Parent<T> { 

    private List<T> item = new ArrayList<T>(); 

    @XmlTransient 
    public List<T> getItem() { 
     return item; 
    } 

    public void setItem(List<T> item) { 
     this.item = item; 
    } 

} 

IntegerChild

package forum7851052; 

import java.util.List; 

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

@XmlRootElement 
public class IntegerChild extends Parent<Integer> { 

    @Override 
    @XmlElement 
    public List<Integer> getItem() { 
     return super.getItem(); 
    } 

    @Override 
    public void setItem(List<Integer> item) { 
     super.setItem(item); 
    } 

} 

StringC Hild

package forum7851052; 

import java.util.List; 

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

@XmlRootElement 
public class StringChild extends Parent<String> { 

    @Override 
    @XmlElement 
    public List<String> getItem() { 
     return super.getItem(); 
    } 

    @Override 
    public void setItem(List<String> item) { 
     super.setItem(item); 
    } 

} 

Demo

package forum7851052; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Marshaller; 

public class Demo { 

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

     Marshaller marshaller = jc.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 

     IntegerChild integerChild = new IntegerChild(); 
     integerChild.getItem().add(1); 
     integerChild.getItem().add(2); 
     marshaller.marshal(integerChild, System.out); 

     StringChild stringChild = new StringChild(); 
     stringChild.getItem().add("A"); 
     stringChild.getItem().add("B"); 
     marshaller.marshal(stringChild, System.out); 
    } 

} 

uscita

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<integerChild> 
    <item>1</item> 
    <item>2</item> 
</integerChild> 
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<stringChild> 
    <item>A</item> 
    <item>B</item> 
</stringChild> 
+0

Cosa succede se il genitore viene utilizzato anche in serializzazione (rendere il transitorio del campo del genitore non è un'opzione)? Questo dovrebbe essere un caso assolutamente valido, tuttavia JAXB sembra utilizzare le annotazioni @ Override. – stuchl4n3k

2

Questo potrebbe essere abbastanza vecchio, ma il suo primo risultato, mentre la ricerca per "JAXB duplicare i campi"

inciampato sullo stesso problema, questo ha fatto il trucco per me:

@XmlRootElement 
@XmlAccessorType(XmlAccessType.NONE) // <-- made the difference 
public abstract class ParentClass 
{ 
... 
} 


@XmlRootElement 
public class ChildClass extends ParentClass 
{ 
... 
} 
Problemi correlati