2012-04-14 9 views
5

aggiornamento - vedi Modifica nella parte inferioreJAXB annotazioni - come faccio a fare una lista di elementi XmlIDRef hanno il valore id come attributo al posto del testo elemento del corpo?

IDREFS/keyrefs sembrano essere possibile in annotazioni JAXB, ma l'arbitro finisce per essere il testo elemento.

desidero l'arbitro di essere un attributo di un elemento.

Ad esempio, dato questo modello oggetto:

@XmlType 
public class Employee { 
    @XmlID 
    @XmlAttribute 
    String name; 
    @XmlAttribute 
    int years; 
    @XmlAttribute 
    String foo; 
} 

@XmlType 
public class Office { 
    @XmlAttribute 
    String name; 
    @XmlElementWrapper 
    @XmlElement(name = "employee") 
    List<Employee> employees; 
} 

@XmlRootElement 
public class Company { 
    @XmlElementWrapper 
    @XmlElement(name = "office") 
    List<Office> offices; 
    @XmlElementWrapper 
    @XmlElement(name = "employee") 
    List<Employee> employees; 
} 

Vorrei il formato XML esteriorizzata per finire per assomigliare a questo:

<company> 
    <offices> 
     <office name="nyc"> 
      <employees> 
       <!--*** id ref to employee name ***--> 
       <employee ref="alice"/> 
       <employee ref="bob"/> 
      </employees> 
     </office> 
     <office name="sf"> 
      <employees> 
       <employee ref="connie"/> 
       <employee ref="daphne"/> 
      </employees> 
     </office> 
    </offices> 
    <employees> 
     <!-- *** name is the id *** --> 
     <employee name="alice" years="3" foo="bar"/> 
     <employee name="bob" years="3" foo="bar"/> 
     <employee name="connie" years="3" foo="bar"/> 
     <employee name="daphne" years="3" foo="bar"/> 
    </employees> 
</company> 

Invece, il meglio che posso fare è questo (con le annotazioni di cui sopra nel codice Java):

<company> 
    <offices> 
     <office name="nyc"> 
      <employees> 
       <employee>alice</employee> 
       <employee>bob</employee> 
      </employees> 
     </office> 
     <office name="sf"> 
      <employees> 
       <employee>connie</employee> 
       <employee>daphne</employee> 
      </employees> 
     </office> 
    </offices> 
    <employees> 
     <employee name="alice" years="3" foo="bar"/> 
     <employee name="bob" years="3" foo="bar"/> 
     <employee name="connie" years="3" foo="bar"/> 
     <employee name="daphne" years="3" foo="bar"/> 
    </employees> 
</company> 

c'è un modo per forzare l'idref valore per essere un attributo del dipendente, piuttosto che il corpo del testo dell'elemento? So che posso farlo con uno schema XML, ma mi piacerebbe attenermi alle annotazioni, se possibile.

Grazie.

Modifica La soluzione di Torious in basso funziona quasi, ma non funziona in alcune circostanze.

unmarshalling ha esito negativo se gli elementi "uffici" vengono prima (nel file XML) gli elementi "dipendenti" che i riferimenti d'ufficio. I riferimenti dipendenti non vengono trovati e il wrapper EmployeeRef ha un oggetto dipendente null. Se il "dipendente" è il primo, funziona.

Questo non sarebbe un gran problema, ma il metodo di marshalling metterà gli "uffici" al primo posto, in modo che il tentativo di non riconoscere ciò che è stato appena scartato fallisca.

modifica 2 commento in risposta di Torious risolve il problema dell'ordine.

risposta

4

La soluzione è quella di utilizzare un XmlAdapter che avvolge un Employee in un'istanza di un nuovo tipo, EmployeeRef, che specifica come mappare l'arbitro XML id:

@XmlType 
public class Office { 

    @XmlAttribute 
    String name; 

    @XmlElementWrapper 
    @XmlElement(name="employee") 
    @XmlJavaTypeAdapter(EmployeeAdapter.class) // (un)wraps Employee 
    List<Employee> employees; 
} 

@XmlType 
public class EmployeeRef { 

    @XmlIDREF 
    @XmlAttribute(name="ref") 
    Employee employee; 

    public EmployeeRef() { 
    } 

    public EmployeeRef(Employee employee) { 
     this.employee = employee; 
    } 
} 

public class EmployeeAdapter extends XmlAdapter<EmployeeRef, Employee> { 

    @Override 
    public EmployeeRef marshal(Employee employee) throws Exception { 
     return new EmployeeRef(employee); 
    } 

    @Override 
    public Employee unmarshal(EmployeeRef ref) throws Exception { 
     return ref.employee; 
    } 
} 

Buona fortuna.

+0

sì, questo ha fatto il trucco. Grazie. – marathon

+0

ok, questo non funziona abbastanza in alcune (più) circostanze. Vedi modifica sopra – marathon

+1

Annota la classe 'Company' con' @XmlType (propOrder = {"dipendenti", "uffici"}) 'per forzare l'ordine desiderato durante il marshalling. – Torious

2

Nota: Sono il lead EclipseLink JAXB (MOXy) e un membro del gruppo di esperti JAXB (JSR-222).

Di seguito è un esempio di come questo può essere fatto con Moxy facendo leva sulla @XmlPath annotazione. A causa di un bug, è necessario utilizzare un'etichetta notte EclipseLink 2.4.0 a partire da 17 maggio 2012. Questa correzione ha anche aggiunto alla EclipseLink 2.3.3 (a partire con il 18 maggio 2012) stream.È possibile scaricare un'etichetta notte dal seguente percorso:

Ufficio

Sulla proprietà employees è possibile utilizzare il @XmlIDREF annotazioni in combinazione con il @XmlPath annotazioni per ottenere il mappatura desiderata. @XmlIDREF indica all'implementazione JAXB di scrivere una chiave esterna anziché l'oggetto.

package forum10150263; 

import java.util.List; 
import javax.xml.bind.annotation.*; 
import org.eclipse.persistence.oxm.annotations.XmlPath; 

@XmlType 
public class Office { 
    @XmlAttribute 
    String name; 

    @XmlPath("employees/employee/@ref") 
    @XmlIDREF 
    List<Employee> employees; 
} 

dipendenti

La contropartita di @XmlIDREF è @XmlID. @XmlID viene utilizzato per specificare la chiave primaria per un oggetto.

package forum10150263; 

import javax.xml.bind.annotation.*; 

@XmlType 
public class Employee { 
    @XmlID 
    @XmlAttribute 
    String name; 

    @XmlAttribute 
    int years; 

    @XmlAttribute 
    String foo; 
} 

Società

Ogni oggetto riferimento tramite il meccanismo @XmlIDREF deve anche essere indicata da un rapporto di contenimento. In questo esempio, questo è realizzato dalla proprietà employees.

package forum10150263; 

import java.util.List; 
import javax.xml.bind.annotation.*; 

@XmlRootElement 
public class Company { 
    @XmlElementWrapper 
    @XmlElement(name = "office") 
    List<Office> offices; 

    @XmlElementWrapper 
    @XmlElement(name = "employee") 
    List<Employee> employees; 
} 

jaxb.properties

Per specificare moxy come provider JAXB è necessario aggiungere un file chiamato jaxb.properties nello stesso pacchetto come il modello di dominio con la seguente voce (vedi Specifying EclipseLink MOXy as Your JAXB Provider)

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory 

Demo

Le API JAXB standard vengono utilizzate per annullare il marshalling dell'XML.

package forum10150263; 

import java.io.File; 
import javax.xml.bind.*; 

public class Demo { 

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

     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     File xml = new File("src/forum10150263/input.xml"); 
     Company company = (Company) unmarshaller.unmarshal(xml); 

     Marshaller marshaller = jc.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
     marshaller.marshal(company, System.out); 
    } 

} 

input.xml/Output

<?xml version="1.0" encoding="UTF-8"?> 
<company> 
    <offices> 
     <office name="nyc"> 
     <employees> 
      <employee ref="alice"/> 
      <employee ref="bob"/> 
     </employees> 
     </office> 
     <office name="sf"> 
     <employees> 
      <employee ref="connie"/> 
      <employee ref="daphne"/> 
     </employees> 
     </office> 
    </offices> 
    <employees> 
     <employee name="alice" years="3" foo="bar"/> 
     <employee name="bob" years="3" foo="bar"/> 
     <employee name="connie" years="3" foo="bar"/> 
     <employee name="daphne" years="3" foo="bar"/> 
    </employees> 
</company> 

Per ulteriori informazioni