5. Générer un jeu de classes à partir d'un schéma

5.1. Utilisation de xjc

La génération d'un jeu de classes utilise un autre utilitaire fourni dans la distribution JAXB : xjc. On peut utiliser le même script Ant que pour la génération du schéma. Il suffit de lui ajouter les éléments suivants.

Exemple 22. Script Ant pour xjc

<!-- définition de la tâche xjc, à ajouter à la suite de 
     la définition de schemagen -->
 <taskdef  name="xjc"  classname="com.sun.tools.xjc.XJCTask">
     <classpath  refid="classpath"/>
 </taskdef>

 <target  name="generate-classes">
     <xjc  destdir="src" 
          package="org.paumard.model.generated"  schema="marin.xsd"/>	
 </target> 

5.2. Exemple de génération de classes par JAXB

La génération de classes ne nous mène pas à un résultat aussi satisfaisant que lorsque l'on génère un schéma. Cela est entre autres dû au fait qu'un schéma XML ne contient pas toutes les informations que l'on avait mises dans les annotations de notre modèle, pour la génération du schéma. Voyons ceci sur un exemple. On se propose de générer un jeu de classes Java à partir du schéma suivant, qui ressemble fort à celui qui a été généré dans la partie précédente.

Exemple 23. Schéma de départ pour xjc

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <xs:schema  version="1.0" 
            xmlns:ns1="http://www.paumard.org/cours-jaxb" 
            xmlns:xs="http://www.w3.org/2001/XMLSchema">

   <xs:complexType  name="bateau">
     <xs:sequence>
       <xs:element  name="nom"  type="xs:string"  minOccurs="0"/>
       <xs:element  name="equipage"  minOccurs="0">
         <xs:complexType>
           <xs:sequence>
             <xs:element  name="equipage"  type="marin"  minOccurs="0"  maxOccurs="10"/>
           </xs:sequence>
         </xs:complexType>
       </xs:element>
     </xs:sequence>
     <xs:attribute  name="id"  type="xs:long"  use="required"/>
   </xs:complexType>

   <xs:complexType  name="marin">
     <xs:sequence>
       <xs:element  name="nom"  type="xs:string"/>
       <xs:element  name="prenom"  type="xs:string"  minOccurs="0"/>
       <xs:element  name="grade"  type="grade"  minOccurs="0"/>
       <xs:element  name="age"  type="xs:long"/>
     </xs:sequence>
     <xs:attribute  name="id"  type="xs:long"  use="required"/>
   </xs:complexType>

   <xs:simpleType  name="grade">
     <xs:restriction  base="xs:string">
       <xs:enumeration  value="MATELOT"/>
       <xs:enumeration  value="BOSCO"/>
       <xs:enumeration  value="PACHA"/>
       <xs:enumeration  value="CUISINIER"/>
     </xs:restriction>
   </xs:simpleType>
 </xs:schema>

L'utilitaire xjc nous génère trois classes pour notre modèle : Marin, Bateau et Grade. Il en génère de plus une quatrième, ObjectFactory, qui est une classe fabrique pour la création des objets de ce modèle. Examinons une version simplifiée des trois classes de notre modèle.

Exemple 24. Classes générées par xjc

//
 // énumération Grade
 @XmlType(name = "grade")
 @XmlEnum
 public enum Grade {

    MATELOT, BOSCO, PACHA, CUISINIER;
    
     // reste de l'énumération
}

 // 
 // classe Bateau
 //
 @XmlAccessorType(XmlAccessType.FIELD)
 @XmlType(name = "bateau", 
         propOrder = {"nom", "equipage"})
 public  class Bateau {

     protected String nom;
     protected Bateau.Equipage equipage;
    
     @XmlAttribute(required = true)
     protected  long id;
    
     @XmlAccessorType(XmlAccessType.FIELD)
     @XmlType(name = "", propOrder = {"equipage"})
     public  static  class Equipage {

         protected List<Marin> equipage;
        
         // reste de la classe membre Equipage
    }
    
     // reste de la classe Bateau
}
 //
 // classe Marin
 //
 @XmlAccessorType(XmlAccessType.FIELD)
 @XmlType(name = "marin", 
         propOrder = {"nom", "prenom", "grade", "age"})
 public  class Marin {

     @XmlElement(required = true)
     protected String nom;
    
     protected String prenom;
    
     protected Grade grade;
    
     protected  long age;
    
     @XmlAttribute(required = true)
     protected  long id;
    
     // reste de la classe Marin
}    

Il n'y a pas grand chose à dire sur la génération de l'énumération Grade. Bien sûr, si l'on a utilisé des annotations @EnumValue pour remplacer les noms de grades par des entiers, cette énumération ne sera pas générée, et le champ grade sera un Integer dans la classe Marin. La classe Marin générée est conforme à ce que l'on pourrait attendre. On remarque simplement que les champs de cette classe sont protected plutôt que private. On constate aussi la génération de l'annotation @XmlType, qui impose l'ordre des champs dans le document XML. Cela est dû au fait que les champs sont spécifiés dans un élément sequence plutôt que all, comme nous l'avions déjà remarqué. La classe Bateau nous réserve un peu plus de surprises, notamment la façon dont le champ equipage a été traité. Nous avons utilisé une annotation @XmlElementWrapper sur le champ equipage de notre classe Bateau originelle, afin de regrouper les marins de notre équipage dans un sous-élément equipage. Bien sûr, xjc ne dispose d'aucune information pour reconstituer cette annotation, et il crée un champ equipage, de type Bateau.Equipage, qui encapsule la liste de marins equipage. Notons également que la contrainte que nous avons posée dans le schéma sur l'élément equipage, et qui empêche son cardinal d'être plus grand que 10 n'a pas été prise en compte dans la génération de la classe Bateau. Au final, on se rend compte que la génération des classes Java est moins précise, et probablement moins exploitable que celle du schéma.

5.3. Utilisation de ObjectFactory

Enfin, remarquons la dernière classe générée par xjc : ObjectFactory. Cette classe est une classe fabrique, qui expose autant de méthodes que nous avons de classes générées. Cette classe est censée être utilisée de la façon suivante.

Exemple 25. Utilisation de ObjectFactory

public  static  void main(String... args) {

     // création d'une instance d'ObjectFactory
    ObjectFactory factory =  new ObjectFactory() ;
    
     // pattern de création d'un marin
    Marin marin = factory.createMarin() ;

     // pattern de création d'un bateau
    Bateau bateau = factory.createBateau() ;
    
     // pattern d'ajout d'un marin à l'équipage
     // 1) création d'un equipage
    Bateau.Equipage equipage = factory.createBateauEquipage() ;
     // 2) création d'un bateau
    Bateau bateau = factory.createBateau() ;
     // 3) initialisation du champ equipage de bateau
    bateau.setEquipage(equipage) ;
     // 4) ajout d'un marin à cet équipage
    bateau.getEquipage().getEquipage().add(marin) ;
}

JAXB et services REST
Retour au blog Java le soir
Cours & Tutoriaux