2012-06-18 8 views
7

Ho trovato alcuni esempi di JAXB2 @XmlRegistry su Internet ma non ci sono buoni tutorial approfonditi che parlano del concetto di usare @XmlRegistry con @XmlElementDecl, mi chiedo se è un concetto poco esplorato in generale.@XmlRegistry - come funziona?

Comunque qui è la mia domanda, prima alcune classi di esempio che sto usando per unmarshall un XML utilizzando JAXB:

La classe principale che sto cercando di unmarshalling utilizzando JAXB - Employee.java

package com.test.jaxb; 

import java.util.List; 

import javax.xml.bind.JAXBElement; 
import javax.xml.bind.annotation.XmlElementDecl; 
import javax.xml.bind.annotation.XmlRegistry; 
import javax.xml.bind.annotation.XmlRootElement; 
import javax.xml.namespace.QName; 

import com.test.jaxb.dto.Address; 

@XmlRootElement 
public class Employee { 
    private int id; 
    private String name; 
    private String email; 

    private List<Address> addresses; 

    public int getId() { 
     return id; 
    } 
    public void setId(int id) { 
     this.id = id; 
    } 
    public String getName() { 
     return name; 
    } 
    public void setName(String name) { 
     this.name = name; 
    } 
    public String getEmail() { 
     return email; 
    } 
    public void setEmail(String email) { 
     this.email = email; 
    } 

    public List<Address> getAddresses() { 
     return addresses; 
    } 
    public void setAddresses(List<Address> addresses) { 
     this.addresses = addresses; 
    } 

    @SuppressWarnings("unused") 
    @XmlRegistry 
    public static class XMLObjectFactory { 
     @XmlElementDecl(scope = Employee.class, name= "id") 
     JAXBElement<String> createEmployeeId(String value) { 
      return new JAXBElement<String>(new QName("id"), String.class, "100"); 
     } 
     @XmlElementDecl(scope = Employee.class, name= "name") 
     JAXBElement<String> createName(String value) { 
      return new JAXBElement<String>(new QName("name"), String.class, "Fake Name"); 
     } 
     @XmlElementDecl(scope = Employee.class, name= "email") 
     JAXBElement<String> createEmail(String value) { 
      return new JAXBElement<String>(new QName("email"), String.class, value); 
     } 

     @XmlElementDecl(scope = Employee.class, name= "addresses") 
     JAXBElement<List> createAddresses(List value) { 
      return new JAXBElement<List>(new QName("addresses"), List.class, value); 
     } 
    } 
} 

la classe bambino - Address.java

package com.test.jaxb.dto; 

import javax.xml.bind.JAXBElement; 
import javax.xml.bind.annotation.XmlElementDecl; 
import javax.xml.bind.annotation.XmlRegistry; 
import javax.xml.bind.annotation.XmlRootElement; 
import javax.xml.namespace.QName; 

import com.test.jaxb.Employee; 

@XmlRootElement 
public class Address { 
    private String addressLine1; 
    private String addressLine2; 
    private String addressLine3; 
    public String getAddressLine1() { 
     return addressLine1; 
    } 
    public void setAddressLine1(String addressLine1) { 
     this.addressLine1 = addressLine1; 
    } 
    public String getAddressLine2() { 
     return addressLine2; 
    } 
    public void setAddressLine2(String addressLine2) { 
     this.addressLine2 = addressLine2; 
    } 
    public String getAddressLine3() { 
     return addressLine3; 
    } 
    public void setAddressLine3(String addressLine3) { 
     this.addressLine3 = addressLine3; 
    } 

    @SuppressWarnings("unused") 
    @XmlRegistry 
    private static class XMLObjectFactory { 
     @XmlElementDecl(scope = Employee.class, name= "addressLine1") 
     JAXBElement<String> createAddressLine1(String value) { 
      return new JAXBElement<String>(new QName("addressLine1"), String.class, value); 
     } 
     @XmlElementDecl(scope = Employee.class, name= "addressLine2") 
     JAXBElement<String> createAddressLine2(String value) { 
      return new JAXBElement<String>(new QName("addressLine2"), String.class, value); 
     } 
     @XmlElementDecl(scope = Employee.class, name= "addressLine3") 
     JAXBElement<String> createAddressLine3(String value) { 
      return new JAXBElement<String>(new QName("addressLine3"), String.class, value); 
     } 
    } 
} 

l'XML da deserializzati - employee.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<employee> 
    <id>1</id> 
    <name>Vaishali</name> 
    <email>[email protected]</email> 
    <addresses> 
     <address> 
      <addressLine1>300</addressLine1> 
      <addressLine2>Mumbai</addressLine2> 
      <addressLine3>India</addressLine3> 
     </address> 
     <address> 
      <addressLine1>301</addressLine1> 
      <addressLine2>Pune</addressLine2> 
      <addressLine3>India</addressLine3> 
     </address> 
    </addresses> 
</employee> 

Codice unmarshalling:

package com.test.jaxb; 

import java.io.FileReader; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 


public class ObjectFactoryTest { 
    public static void main(String[] args) throws Exception { 
     FileReader reader = new FileReader("resources/employee.xml"); 
     JAXBContext context = JAXBContext.newInstance(Employee.class); 
     Unmarshaller unmarshaller = context.createUnmarshaller(); 
     Object obj = unmarshaller.unmarshal(reader); 
     System.out.println(obj); 
    } 
} 

Quando ho Unmarshal XML dipendente utilizza sopra il codice, l'elenco di indirizzi non ottiene popolata. L'oggetto dipendente risultante ha solo una lista vuota di indirizzi. C'è qualcosa di sbagliato nelle mie mappature?

Per scoprire cosa sta succedendo e vedere se gli oggetti dipendenti vengono effettivamente creati utilizzando l'Object Factory (con l'annotazione @XMLRegistry), ho modificato il valore di id e name nei metodi factory, tuttavia ciò non ha avuto alcun effetto nell'output, che mi dice che JAXB non sta effettivamente utilizzando ObjectFactory, perché?

Sto uscendo tutto sbagliato? Qualsiasi aiuto sarebbe apprezzato.

risposta

15

@XmlRegistry - come funziona?

@XmlRegistry viene utilizzato per contrassegnare una classe con annotazioni @XmlElementDecl. Per fare in modo che l'implementazione JAXB esegua questo corso, è necessario assicurarsi che sia incluso nell'elenco delle classi utilizzate per eseguire il boot di JAXBContext. Non è sufficiente per essere una classe interna statica di una delle classi modello di dominio:

JAXBContext context = JAXBContext.newInstance(Employee.class, Employee.XMLObjectFactory.class); 

@XmlElementDecl - come funziona?

Se il valore del campo/proprietà sta per essere un JAXBElement allora avete bisogno di sfruttare @XmlElementDecl. Un JAXBElement acquisizione di informazioni può essere utile:

  • Nome elemento, ciò è necessario se si è la mappatura di una struttura di scelta in cui più elementi sono dello stesso tipo. Se il nome dell'elemento non corrisponde a un tipo unico, non sarà possibile eseguire il round-trip del documento.
  • JAXBElement può essere utilizzato per rappresentare un elemento con xsi:nil="true".

XmlObjectFactory

@XmlElementDecl consente anche di specificare un ambito. Ho modificato il modello da voi postare un pò. Ho introdotto una classe XmlObjectFactory che ha due @XmlElementDecl. Entrambi specificano un nome di address. Ho sfruttato la proprietà scope in modo che per le proprietà all'interno della classe Employee sia utilizzato lo @XmlElementDecl corrispondente alla classe Address.

package forum11078850; 

import javax.xml.bind.JAXBElement; 
import javax.xml.bind.annotation.XmlElementDecl; 
import javax.xml.bind.annotation.XmlRegistry; 
import javax.xml.namespace.QName; 

@XmlRegistry 
public class XmlObjectFactory { 

    @XmlElementDecl(scope = Employee.class, name = "address") 
    JAXBElement<Address> createAddress(Address value) { 
     return new JAXBElement<Address>(new QName("address"), Address.class, value); 
    } 

    @XmlElementDecl(name = "address") 
    JAXBElement<String> createStringAddress(String value) { 
     return new JAXBElement<String>(new QName("address"), String.class, value); 
    } 

} 

Dipendente

Il @XmlElementRef nota causerà il valore della proprietà da abbinare sul suo nome elemento radice. Le possibili corrispondenze includeranno classi mappate con @XmlRootElement o @XmlElementDecl.

package forum11078850; 

import java.util.List; 

import javax.xml.bind.JAXBElement; 
import javax.xml.bind.annotation.*; 

@XmlRootElement 
@XmlType(propOrder = { "id", "name", "email", "addresses" }) 
public class Employee { 
    private int id; 
    private String name; 
    private String email; 

    private List<JAXBElement<Address>> addresses; 

    public int getId() { 
     return id; 
    } 

    public void setId(int id) { 
     this.id = id; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getEmail() { 
     return email; 
    } 

    public void setEmail(String email) { 
     this.email = email; 
    } 

    @XmlElementWrapper 
    @XmlElementRef(name="address") 
    public List<JAXBElement<Address>> getAddresses() { 
     return addresses; 
    } 

    public void setAddresses(List<JAXBElement<Address>> addresses) { 
     this.addresses = addresses; 
    } 

} 

ObjectFactoryTest

package forum11078850; 

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

public class ObjectFactoryTest { 
    public static void main(String[] args) throws Exception { 
     FileReader reader = new FileReader("src/forum11078850/input.xml"); 
     JAXBContext context = JAXBContext.newInstance(Employee.class, XmlObjectFactory.class); 
     Unmarshaller unmarshaller = context.createUnmarshaller(); 
     Object obj = unmarshaller.unmarshal(reader); 

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

La classe Address e input.xml dalla mia risposta iniziale può essere utilizzato per eseguire questo esempio.


RISPOSTA ORIGINALE

io non sono sicuro di come si sta tentando di utilizzare @XmlRegistry, quindi mi concentrerò sulla seguente parte del tuo post:

Quando ho Unmarshal il xml dipendente che utilizza il codice precedente, l'elenco di indirizzi non viene popolato. L'oggetto dipendente risultante ha solo un elenco vuoto di indirizzi . C'è qualcosa di sbagliato nelle mie mappature?

Il tuo elenco di oggetti Address è avvolto in un elemento di raggruppamento (addresses), quindi è necessario utilizzare il @XmlElementWrapper annotazioni per mappare questo caso d'uso. Di seguito è riportato un esempio completo:

dipendenti

package forum11078850; 

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

@XmlRootElement 
@XmlType(propOrder = { "id", "name", "email", "addresses" }) 
public class Employee { 
    private int id; 
    private String name; 
    private String email; 

    private List<Address> addresses; 

    public int getId() { 
     return id; 
    } 

    public void setId(int id) { 
     this.id = id; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getEmail() { 
     return email; 
    } 

    public void setEmail(String email) { 
     this.email = email; 
    } 

    @XmlElementWrapper 
    @XmlElement(name = "address") 
    public List<Address> getAddresses() { 
     return addresses; 
    } 

    public void setAddresses(List<Address> addresses) { 
     this.addresses = addresses; 
    } 

} 

Indirizzo

package forum11078850; 

public class Address { 
    private String addressLine1; 
    private String addressLine2; 
    private String addressLine3; 

    public String getAddressLine1() { 
     return addressLine1; 
    } 

    public void setAddressLine1(String addressLine1) { 
     this.addressLine1 = addressLine1; 
    } 

    public String getAddressLine2() { 
     return addressLine2; 
    } 

    public void setAddressLine2(String addressLine2) { 
     this.addressLine2 = addressLine2; 
    } 

    public String getAddressLine3() { 
     return addressLine3; 
    } 

    public void setAddressLine3(String addressLine3) { 
     this.addressLine3 = addressLine3; 
    } 

} 

ObjectFactoryTest

package forum11078850; 

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

public class ObjectFactoryTest { 
    public static void main(String[] args) throws Exception { 
     FileReader reader = new FileReader("src/forum11078850/input.xml"); 
     JAXBContext context = JAXBContext.newInstance(Employee.class); 
     Unmarshaller unmarshaller = context.createUnmarshaller(); 
     Object obj = unmarshaller.unmarshal(reader); 

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

input.xml/Output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<employee> 
    <id>1</id> 
    <name>Vaishali</name> 
    <email>[email protected]</email> 
    <addresses> 
     <address> 
      <addressLine1>300</addressLine1> 
      <addressLine2>Mumbai</addressLine2> 
      <addressLine3>India</addressLine3> 
     </address> 
     <address> 
      <addressLine1>301</addressLine1> 
      <addressLine2>Pune</addressLine2> 
      <addressLine3>India</addressLine3> 
     </address> 
    </addresses> 
</employee> 
+0

Ho trovato diversi esempi di '@XmlRegistry' utilizzato allo stesso modo in cui lo sto usando, guarda [qui] (http://www.codezealot.org/archives/5). Capisco che posso usare '@XmlElementWrapper' per fare questo. La mia domanda riguardava la comprensione di cosa sia "@XmlRegistry" e cosa potessi fare con esso. AFAIK I dovrebbe essere in grado di usare objectfactory (annotato con '@XmlRegistry') per unmarshal unmarshal e convertirlo in un oggetto. In caso contrario, a cosa serve? Devo registrarlo da qualche parte per poterlo utilizzare (come ho dimostrato nel mio esempio, la mia fabbrica di oggetti non è mai realmente utilizzata da JAXB)? – gresdiplitude

+0

La mia comprensione potrebbe essere completamente errata qui, ma ho pensato che ci deve essere un modo per applicare qualcosa di simile a "@XmlElementWrapper" a "@XmlRegistry", in quanto è un modo alternativo di unmarshalling XML. Nessun punto per indovinare Sono totalmente confuso come funziona @XmlRegistry !!! – gresdiplitude

+0

@Vaishali - Ho aggiornato la mia risposta per includere un esempio di '@ XmlRegistry' e' @ XmlElementDecl' –

-1

Devi prendere un oggetto List of Address. In quell'oggetto, dovrai aggiungere l'oggetto che contiene dati come addressline1. indirizzo2 e così via.

i.e. 
List addrObjList = new List(); 
addrObjList.add(object); // Bind an object containing data and add one by one 
+0

Due cose, uno - che non dovrebbe essere richiesto come classe Indirizzo stessa è annotato con @XmlRootElement e ha una fabbrica di oggetto a sé stante, JAXB dovrebbe essere in grado di farlo automaticamente ...e due - come ho detto nella mia domanda, JAXB sembra non utilizzare affatto la fabbrica di oggetti, quindi qualsiasi modifica apportata ai metodi di fabbrica non ha alcun effetto. – gresdiplitude