2012-06-18 1 views
7

J'ai trouvé quelques exemples de JAXB2 @XmlRegistry sur internet mais pas de bons tutoriels approfondis qui parlent du concept d'utilisation @XmlRegistry avec @XmlElementDecl, je me demande si c'est un concept peu exploré en général.@XmlRegistry - comment ça marche?

Quoi qu'il en soit, ma question est, d'abord quelques exemples de cours que j'utilise pour unmarshall un fichier XML en utilisant JAXB:

La classe principale, je suis en train de désorganiser l'utilisation 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 enfant - 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); 
     } 
    } 
} 

xml à unmarshalled - 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> 

code 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); 
    } 
} 

Quand j'unmarshal le xml employé en utilisant le code ci-dessus, la liste d'adresses ne soit pas peuplée. L'objet employé résultant n'a qu'une liste vide d'adresses. Y a-t-il un problème avec mes mappings?

Pour savoir ce qui se passe et voir si les objets employés sont réellement créés à l'aide de Object Factory (avec l'annotation @XMLRegistry), j'ai changé la valeur de id et name dans les méthodes d'usine, mais cela n'a aucun effet dans la sortie, qui me dit que JAXB n'utilise pas réellement l'ObjectFactory, pourquoi?

Est-ce que je vais là-dessus tout faux? Toute aide serait appréciée.

Répondre

15

@XmlRegistry - comment ça marche?

@XmlRegistry est utilisé pour marquer une classe qui a @XmlElementDecl annotations. Pour que votre implémentation JAXB traite cette classe, vous devez vous assurer qu'elle est incluse dans la liste des classes utilisées pour amorcer le JAXBContext. Il ne suffit pas pour qu'elle soit une classe interne statique d'un de vos classes de modèle de domaine:

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

@XmlElementDecl - Comment ça marche?

Si la valeur du champ/la propriété va être un JAXBElement alors vous devez tirer parti @XmlElementDecl. Une JAXBElement capture des informations qui est peut être utile:

  • Nom de l'élément, cela est nécessaire si vous mappez à une structure de choix où plusieurs éléments sont du même type. Si le nom de l'élément ne correspond pas à un type unique, vous ne pourrez pas inverser le document.
  • JAXBElement peut être utilisé pour représenter un élément avec xsi:nil="true".

XmlObjectFactory

@XmlElementDecl vous permet également de spécifier une portée. J'ai modifié le modèle de vous poster un peu. J'ai introduit une classe XmlObjectFactory qui a deux @XmlElementDecl. Les deux spécifient un nom de address. J'ai utilisé la propriété scope pour que, pour les propriétés de la classe Employee, la classe @XmlElementDecl correspondant à la classe Address soit utilisée.

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); 
    } 

} 

employé

L'annotation @XmlElementRef entraînera la valeur de la propriété à apparier sur son nom de l'élément racine. Les correspondances possibles incluront des classes mappées avec @XmlRootElement ou @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 et input.xml de ma première réponse peut être utilisé pour exécuter cet exemple.


ORIGINAL RÉPONSE

Je ne sais pas comment vous essayez d'utiliser @XmlRegistry, alors je vais mettre l'accent sur la partie suivante de votre message:

Quand j'unmarshal la Employé xml en utilisant le code ci-dessus, la liste d'adresses n'est pas remplie. L'objet employé résultant n'a qu'une liste vide d'adresses . Y a-t-il un problème avec mes mappings?

Votre liste d'objets Address est enveloppé dans un élément de regroupement (addresses), vous devez donc utiliser l'annotation @XmlElementWrapper pour cartographier ce cas d'utilisation. Ci-dessous un exemple complet:

employé

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; 
    } 

} 

Adresse

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/sortie

<?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

J'ai trouvé plusieurs exemples de '@XmlRegistry' utilisé de la même manière que je l'utilise, regardez [ici] (http://www.codezealot.org/archives/5). Je comprends que je peux utiliser '@XmlElementWrapper' pour le faire. Ma question était plus sur la compréhension de ce que '@XmlRegistry' est et ce que je peux faire avec. Je pense que je devrais être capable d'utiliser objectfactory (annoté avec '@XmlRegistry') pour démélanger un fichier xml et le convertir en objet. Si non, à quoi sert-il? Dois-je l'enregistrer quelque part pour qu'il soit utilisé (comme je le montre dans mon exemple, mon usine d'objets n'est jamais vraiment utilisée par JAXB)? – gresdiplitude

+0

Ma compréhension pourrait être complètement erronée ici, mais je pensais qu'il devait y avoir une façon d'appliquer quelque chose de similaire à '@XmlElementWrapper' à '@XmlRegistry', car c'est une manière alternative de désarchivage XML. Pas de points pour deviner je suis totalement confus comment fonctionne @XmlRegistry !!! – gresdiplitude

+0

@Vaishali - J'ai mis à jour ma réponse pour inclure un exemple en utilisant '@ XmlRegistry' et' @ XmlElementDecl' –

-1

Vous devez prendre un objet List de l'adresse. Dans cet objet, vous devrez ajouter l'objet qui contient des données comme addressline1. addressline2 et ainsi de suite.

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

Deux choses, un - qui ne devrait pas être nécessaire que la classe d'adresse elle-même est annotée avec @XmlRootElement et a une usine d'objet propre, JAXB devrait être en mesure de le faire automatiquement ...et deux - comme je l'ai mentionné dans ma question, JAXB ne semble pas du tout utiliser l'Object Factory, donc tout changement apporté aux méthodes d'usine n'a aucun effet. – gresdiplitude