4. Générer un schéma à partir de classes

4.1. Utilisation de schemagen

Une classe annotée peut être utilisée pour générer un schéma XML. Les documents XML générés comme nous l'avons vu dans la partie précédente seront valides par rapport à ce schéma. Pour cette génération, nous avons besoin d'un utilitaire Java, qui fait partie de la distribution de JAXB. Cette distribution est disponible sur le site de référence de JAXB. Cet utilitaire s'appelle schemagen, et peut s'utiliser au travers d'une tâche Ant, ou d'un plugin Maven. Écrivons un script Ant simple permettant d'utiliser le générateur de schéma.

Exemple 16. Script Ant pour schemagen

<?xml version="1.0" encoding="UTF-8"?>
 <project  name="generate-schema"  default="generate-schema"  basedir=".">

     <property  name="jaxb.home"  value="D:/java/JAXB-2.1/jaxb-ri-20081030"/>

     <!-- définition d’un classpath sur la distribution -->
     <path  id="classpath">
         <fileset  dir="${jaxb.home}"  includes="lib/*.jar" />
     </path>

     <!-- définition d’une tâche Ant schemagen -->
     <taskdef  name="schemagen"  classname="com.sun.tools.jxc.SchemaGenTask">
         <classpath  refid="classpath"/>
     </taskdef>

     <!-- écriture de la cible Ant -->
     <target  name="generate-schema">
         <!-- src et destdit indiquent l'endroit où se trouvent les fichiers source
             et le répertoire où les fichiers XSD vont être créés -->
         <schemagen  srcdir="../src"  destdir="../schemas">
             <!-- on peut fixer le nom du fichier XSD, ainsi que l'espace de noms -->
             <schema  file="marin.xsd"  namespace="http://www.paumard.org/cours-jaxb"/>
             <!-- on peut exclure des packages-->
             <exclude  name="**/test/**"/>
             <exclude  name="**/main/**"/>
         </schemagen>
     </target>

 </project>

Ce script comporte quatre éléments. Les trois premières tâches sont génériques : elles sont paramétrées par le répertoire d'installation de JAXB (que l'on choisit). À ceci près, elles ne varient pas d'un projet à l'autre.
  • Tout d'abord la définition d'une propriété jaxb.home. Cette propriété référence le répertoire dans lequel nous avons ouvert l'archive JAXB téléchargée sur Java.net.
  • Ensuite, la définition d'un classpath . Ce classpath référence l'ensemble des JAR qui composent la distribution JAXB.
  • Ensuite la définition d'une tâche Ant, schemagen. C'est cette tâche qui va nous permettre de lancer la génération de schéma XML.
Le dernier élément, lui, dépend du projet que l'on traite. Remarquons que seules les classes du modèle sont utiles à la génération du schéma. Netbeans supporte complètement Ant, et permet d'éditer ce genre de fichier très facilement. Il permet également de lancer l'exécution de tâches Ant directement depuis l'interface. L'élément schema permet de fixer le nom du fichier XSD généré pour le schéma donné en paramètre. Si ce nom de schéma ne correspond pas au schéma fixé par l'annotation @XmlRootElement, alors le nom du fichier n'est pas utilisé.

4.2. Exemple de génération d'un schéma par JAXB

Prenons en exemple le modèle suivant.

Exemple 17. Modèle pour la génération d'un schéma

//
 // énumération Grade
 //
 public enum Grade {

    MATELOT, BOSCO, PACHA, CUISINIER
}

 //
 // classe Marin
 //
 @XmlRootElement(name="marin", namespace="http://www.paumard.org/cours-jaxb")
 @XmlAccessorType(XmlAccessType.FIELD)
 @XmlType(propOrder={"nom", "prenom", "grade", "age"})
 public  class Marin {

     @XmlAttribute(name="id")
     private  long id ;

     @XmlElement(required=true)
     private String nom ;

     private String prenom ;
     private  long age ;

     private Grade grade ;
    
     // reste de la classe 
}

 //
 // classe Bateau
 //
 @XmlRootElement(name="bateau")
 @XmlAccessorType(XmlAccessType.FIELD)
 public  class Bateau {

     @XmlAttribute
     private  long id ;

     private String nom ;

     @XmlElementWrapper(name="equipage")
     private List<Marin> equipage =  new ArrayList<Marin>() ;
    
     // reste de la classe
}

JAXB nous génère deux schémas, qui se référencent l'un l'autre. Le premier schéma est enregistré comme prévu dans le fichier marin.xsd. Il définit l'élément racine marin, dans le bon espace de noms.

Exemple 18. Schéma généré, fichier marin.xsd

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

   <xs:import  schemaLocation="schema2.xsd"/>

   <xs:element  name="marin"  type="marin"/>

 </xs:schema>

Exemple 19. Schéma généré, fichier schema2.xsd

<?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:import  namespace="http://www.paumard.org/cours-jaxb" 
              schemaLocation="marin.xsd"/>

   <xs:element  name="bateau"  type="bateau"/>

   <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"  nillable="true" 
                         minOccurs="0"  maxOccurs="unbounded"/>
           </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>

JAXB nous a généré deux types complexes pour les classes Bateau et Marin. La lecture de ces types complexes est immédiate : les différents champs des classes sont associés à des éléments XML, et l'on retrouve bien la définition de l'ID en tant qu'attribut. Notons que les énumérations sont parfaitement supportées par JAXB : la définition de Grade est correcte. Remarquons la façon dont JAXB a pris en compte les contraintes que nous avons posées sur la classe Marin, en comparaison de la classe Bateau.
  • Nous avons imposé l'ordre des éléments XML de l'élément Marin, et il a bien été pris en compte. On remarque que l'ordre est également imposé dans la classe Capitaine, alors que nous ne l'avons pas demandé. Effectivement, l'élément xs:sequence impose que l'ordre des éléments indiqués soient respecté. Ce n'est pas le cas pour xs:all. Il est important de noter ce point.
  • Nous avons imposé que le champ nom de la classe Marin soit toujours présent. Ce n'est pas le cas pour le même champ de la classe Bateau. De fait, dans le schéma XML, on constate que l'élément nom peut être absent de l'élément bateau. Il doit être présent dans l'élément marin (attribut minOccurs).
Enfin, on peut examiner l'effet de l'utilisation de l'annotation @XmlEnumValue sur la classe Grade. Rappelons que cette annotation impose la valeur de chaque élément de l'énumération.

Exemple 20. Enumération annotée avec @XmlEnumValue

public enum Grade {

     @XmlEnumValue("10")
    MATELOT, 
    
     @XmlEnumValue("20")
    BOSCO,

     @XmlEnumValue("30")
    PACHA,
            
     @XmlEnumValue("40")
    CUISINIER
}

L'élément de schéma XML généré est le suivant.

Exemple 21. Schéma généré avec @XmlEnumValue

   <xs:simpleType  name="grade">
     <xs:restriction  base="xs:string">
       <xs:enumeration  value="10"/>
       <xs:enumeration  value="20"/>
       <xs:enumeration  value="30"/>
       <xs:enumeration  value="40"/>
     </xs:restriction>
   </xs:simpleType>

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