IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Format d'échange (XML, JSON...) Java Discussion :

Générer un fichier XML à partir d'annotations


Sujet :

Format d'échange (XML, JSON...) Java

  1. #1
    Rédacteur
    Avatar de thierryler
    Homme Profil pro
    Inscrit en
    Octobre 2007
    Messages
    4 078
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 078
    Points : 12 815
    Points
    12 815
    Par défaut Générer un fichier XML à partir d'annotations
    Bonjour à tous,

    Je voudrais générer un fichier XML à partie d'une classe sur laquelle j'aurais mis des annotations.

    Je bosse avec Java 8 et Maven 3. La génération peut se faire lors de la compil Maven.

    Mon bean est le suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    public class ChienService {
     
    	public double add(String race, int age) {
    		...
    	}
     
    	...
    }
    Et je voudrais que ça génère un fichier comme dans l'exemple suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    <documentation>
    	<obj name="chien">
    		<operations>
    			<operation name="ajouter">
    				<params>
    					<param io="in" name="race" type="String" />
    					<param io="in" name="age" type="int" />
    					<param io="out" name="prix" type="double" />
    				</params>
     
    				...
    			</operation>
    		</operations>
    	</obj>
    </documentation>
    Je me suis donc fait un jeu d'annotations :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public @interface ToXml {
    	String name();
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    public @interface Operation {
    	String name();
    	String type();
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    public @interface OpeParam {
    	String name();
    	String type();
    }
    Et je l'utilise comme suit:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    @ToXml(name="chien")
    public class ChienService {
     
    	@Operation(name="ajouter", type="num")
    	public double add(
    			@OpeParam(name="race", type="text") 
    			String race, 
    			@OpeParam(name="age", type="num") 
    			int age) {
    		...
    	}
     
    	@Operation(name="acheter", type="text")
    	public String buy(
    			@OpeParam(name="id", type="num") 
    			String identifiant {
    		...
    	}
     
    	...
    }
    Je pense que c'est largement suffisant.

    Par contre, je ne sais pas comment générer le fichier XML et automatiser dans Maven à partir de tout ça... Quelqu'un pourrait m'aider ?

    Merci d'avance,
    Thierry
    Thierry Leriche-Dessirier
    Consultant Java JEE Web Agile freelance
    Rédacteur pour Developpez
    Professeur de Génie Logiciel à l'ESIEA

    Site : http://www.icauda.com / Linked'in : http://www.linkedin.com/in/thierryler / Twitter : @ThierryLeriche

  2. #2
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    heu, tu fais un plugin maven pour ça?

  3. #3
    Rédacteur
    Avatar de thierryler
    Homme Profil pro
    Inscrit en
    Octobre 2007
    Messages
    4 078
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 078
    Points : 12 815
    Points
    12 815
    Par défaut
    En fait je ne sais pas trop par quel bout prendre cette affaire. Je pensais à Maven histoire que ce soit automatisé. Mais si ça peut être du pur java au runtime, ça me va aussi.
    Thierry Leriche-Dessirier
    Consultant Java JEE Web Agile freelance
    Rédacteur pour Developpez
    Professeur de Génie Logiciel à l'ESIEA

    Site : http://www.icauda.com / Linked'in : http://www.linkedin.com/in/thierryler / Twitter : @ThierryLeriche

  4. #4
    Membre averti
    Homme Profil pro
    Dev
    Inscrit en
    Novembre 2006
    Messages
    112
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev

    Informations forums :
    Inscription : Novembre 2006
    Messages : 112
    Points : 350
    Points
    350
    Par défaut
    salut

    rajoute à ton annotation @Retention(RetentionPolicy.RUNTIME) pour l'avoir au runtime.

    tu peut regarder du coté de APT pour faire des traitements au moment de la compilation .

  5. #5
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,

    Citation Envoyé par miaous Voir le message
    tu peut regarder du coté de APT pour faire des traitements au moment de la compilation .
    Gros +1 cela me semble plus approprié.
    Le fichier pourra être généré automatiquement à la compilation, et tu pourras rajouter des contraintes supplémentaire en générant des warnings/erreurs de compilation !
    En plus tu as accès à plus d'information que via la reflection, puisque tu utilises l'API "Mirror" qui se base sur le code source (et non pas le bytecode). En particulier tu peux récupérer le nom des paramètres.


    Exemple complet :

    Je défini l'annotation @ToXml ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.adiguba.xml;
     
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Target;
     
    @Target(ElementType.TYPE)
    public @interface ToXml {
     
    }
    L'annotation @Target indique qu'elle ne peut être utiliser que sur un type (classe/interface/enum). Elle permettra de marquer les types pour lesquels on voudra générer un fichier XML.


    Et une annotation @XmlAlias qui permettra de donner un autre nom à une méthode ou paramètre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    package com.adiguba.xml;
     
    public @interface XmlAlias {
    	String value();
    }


    Ensuite il faut définir un Processor.
    Sa méthode process() sera appelé pour traiter les annotations qu'il traite, et on peut alors utiliser l'API javax.lang.model pour analyser le code.

    On peut utiliser AbstractProcessor pour se simplifier un peu la tâche.
    Dans notre cas cela donnerait quelque chose comme cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    package com.adiguba.xml;
     
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.Writer;
    import java.util.Set;
     
    import javax.annotation.processing.AbstractProcessor;
    import javax.annotation.processing.RoundEnvironment;
    import javax.annotation.processing.SupportedAnnotationTypes;
    import javax.annotation.processing.SupportedSourceVersion;
    import javax.lang.model.SourceVersion;
    import javax.lang.model.element.Element;
    import javax.lang.model.element.ElementKind;
    import javax.lang.model.element.ExecutableElement;
    import javax.lang.model.element.NestingKind;
    import javax.lang.model.element.PackageElement;
    import javax.lang.model.element.TypeElement;
    import javax.lang.model.element.VariableElement;
    import javax.lang.model.type.DeclaredType;
    import javax.lang.model.type.TypeMirror;
    import javax.tools.Diagnostic.Kind;
    import javax.tools.FileObject;
    import javax.tools.StandardLocation;
     
    // Indique la (ou les) annotation(s) que l'on supporte :
    @SupportedAnnotationTypes("com.adiguba.xml.ToXml")
    // Indique la version des sources que l'on supporte :
    @SupportedSourceVersion(SourceVersion.RELEASE_7)
    // On peut même définir des options à passer via l'option -A de javac
    // @SupportedOptions()
    public class ToXmlProcessor extends AbstractProcessor {
     
     
    	@Override
    	public boolean process(Set<? extends TypeElement> annotations,
    			RoundEnvironment roundEnv) {
    		// On traite les annotations que l'on a déclarer :
    		for (TypeElement annotation : annotations) {
    			// On récupère tous les éléments annotés par l'annotation en question :
    			for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
    				// S'il s'agit d'un Type, on construit le XML :
    				if (element instanceof TypeElement) {
    					buildXml((TypeElement)element);
    				}
    			}
    		}
    		return false;
    	}
     
    	private void buildXml(TypeElement type) {
    		try {
     
    			// On génère une erreur de compilation si c'est une classe interne :
    			if (type.getNestingKind()!=NestingKind.TOP_LEVEL) {
    				processingEnv.getMessager().printMessage(Kind.ERROR,
    						"Annotation @ToXml can only be used on top-level type",
    						type);
    				return;
    			}
     
    			// On recherche le nom du type (ou celui indiqué dans l'annoation) : 
    			final String name = getXmlAlias(type);
     
    			// On récupère le nom du package
    			// (ou la classe conteneur si on est dans un type interne, ce qui n'est pas le cas ici)
    			String packageName = ((PackageElement)type.getEnclosingElement()).getQualifiedName().toString();
     
     
    			// On génère un fichier dans le répertoire des classes :
    			FileObject file = processingEnv.getFiler().createResource(
    					StandardLocation.CLASS_OUTPUT,	
    					packageName, name + ".xml", type);
     
    			// On génère le document XML :
    			try (Writer writer = file.openWriter();
    				PrintWriter pw = new PrintWriter(writer)) {
    				pw.println("<document>");
    				pw.println("	<doc name='" + name + "'>");
    				pw.println("		<operations>");
    				// On parcours tous les éléments du type :
    				for (Element e : type.getEnclosedElements()) {
    					// S'il s'agit d'une méthode :
    					if (e.getKind()==ElementKind.METHOD) {
    						ExecutableElement method = (ExecutableElement)e;
     
    						String opName = getXmlAlias(method);
    						pw.println("			<operation name='" + opName + "'>");
    						pw.println("				<params>");
    						// Pour chaque paramètre de la méthode :
    						for (VariableElement var : method.getParameters()) {
    							// On récupère le nom du paramètre
    							String varName = getXmlAlias(var);
    							// On récupère le type du paramètre
    							String varType = getTypeName(var.asType());
     
    							pw.print("					");
    							pw.println("<params io='in' name='"
    									+ varName + "' type='" + varType + "'/>");
    						}
    						// Et on écrit le type de retour :
    						TypeMirror returnType = method.getReturnType();
    						String retName = "ret"; // Devrait-on utiliser une annotation pour cela ?
    						String retType = getTypeName(returnType);
    						pw.print("					");
    						pw.println("<params io='out' name='"
    								+ retName + "' type='" + retType + "'/>");
     
    						pw.println("				</params>");
    						pw.println("			</operation>");
    					}
    				}
    				pw.println("		</operations>");
    				pw.println("	</doc>");
    				pw.println("</document>");	
    			}
    			// On affiche une note de compilation :
    			processingEnv.getMessager().printMessage(Kind.WARNING,
    				"Build " + file.getName() + " in " + packageName);
     
    		} catch (IOException e) {
    			processingEnv.getMessager().printMessage(Kind.ERROR, "IO Error:" + e.getMessage(), type);
    		}
    	}
     
    	/**
             * Retourne le nom de la variable, éventuellement selon l'annotation XmlAlias.
             */
    	private String getXmlAlias(Element elem) {
    		XmlAlias alias = elem.getAnnotation(XmlAlias.class);
    		if (alias!=null) {
    			return alias.value();
    		}
    		return elem.getSimpleName().toString();
    	}
     
    	/**
             * Retourne le nom du type.
             */
    	private String getTypeName(TypeMirror type) {
    		switch (type.getKind()) {
    		case DECLARED:
    			// C'est une déclaration de type, on utilise simpleName pour obtenir le nom court
    			// (cad "String" à la place de "java.lang.String").
    			// Mais on pourrait opter pour une liste de type accepté par exemple...
    			return ((DeclaredType)type).asElement().getSimpleName().toString();
    		default:
    		}
    		// Par défaut on utilise toString(), qui marche très bien pour les primitifs.
    		// Mais on pourrait être amené à devoir gérer des cas particulier.
    		return type.toString();
    	}
    }

    Il faut désormais indiquer à javac qu'on souhaite utiliser ce processor.
    On peut utiliser pour cela l'option -processor mais le mieux consiste à créer un jar contenant les annotations + le processor, et de le définir en service.
    Pour cela il faut créer un fichier "META-INF/services/javax.annotation.processing.Processor" dans le jar, et d'y indiquer le nom complet du processor :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    com.adiguba.xml.ToXmlProcessor
    Le compilateur javac chargera ainsi automatiquement le processor à la compilation.
    Je n'ai pas testé avec Maven mais si la compilation utilise javac ca devrait marcher. Par contre certains EDI (comme eclipse) nécessite un paramétrage spécifique.




    La compilation de ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.adiguba.io;
     
    import com.adiguba.myapps.*;
     
    @ToXml @XmlAlias("chien")
    public class ChienService {
     
    	@XmlAlias("ajouter")
    	public double add(String race, int age) {
    		// code ... 
    		//
    		return 10.0;
    	}
     
    	public String buy(@XmlAlias("id") String identifiant) {
    		// code ...
    		return null;
    	}
    }
    me génère ainsi un fichier de la sorte :
    Code XML : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <document>
    	<doc name='chien'>
    		<operations>
    			<operation name='ajouter'>
    				<params>
    					<params io='in' name='race' type='String'/>
    					<params io='in' name='age' type='int'/>
    					<params io='out' name='ret' type='double'/>
    				</params>
    			</operation>
    			<operation name='buy'>
    				<params>
    					<params io='in' name='id' type='String'/>
    					<params io='out' name='ret' type='String'/>
    				</params>
    			</operation>
    		</operations>
    	</doc>
    </document>


    a++

  6. #6
    Rédacteur
    Avatar de thierryler
    Homme Profil pro
    Inscrit en
    Octobre 2007
    Messages
    4 078
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 078
    Points : 12 815
    Points
    12 815
    Par défaut
    Waouuuu, ça c'est du super bon code d'exemple. Je n'en espérais pas tant... Merci beaucoup.
    Thierry Leriche-Dessirier
    Consultant Java JEE Web Agile freelance
    Rédacteur pour Developpez
    Professeur de Génie Logiciel à l'ESIEA

    Site : http://www.icauda.com / Linked'in : http://www.linkedin.com/in/thierryler / Twitter : @ThierryLeriche

  7. #7
    Rédacteur
    Avatar de thierryler
    Homme Profil pro
    Inscrit en
    Octobre 2007
    Messages
    4 078
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 078
    Points : 12 815
    Points
    12 815
    Par défaut
    Il faut désormais indiquer à javac qu'on souhaite utiliser ce processor.
    On peut utiliser pour cela l'option -processor mais le mieux consiste à créer un jar contenant les annotations + le processor, et de le définir en service.
    Pour cela il faut créer un fichier "META-INF/services/javax.annotation.processing.Processor" dans le jar, et d'y indiquer le nom complet du processor :
    Code :Sélectionner tout - Visualiser dans une fenêtre à part
    com.adiguba.xml.ToXmlProcessor
    Le compilateur javac chargera ainsi automatiquement le processor à la compilation.
    Je n'ai pas testé avec Maven mais si la compilation utilise javac ca devrait marcher. Par contre certains EDI (comme eclipse) nécessite un paramétrage spécifique.
    Je n'ai pas bien compris cette partie. D'abord, je dois créer un jar avec juste mes annotations + processeur ? Et comment lancer depuis eclipse ?
    Thierry Leriche-Dessirier
    Consultant Java JEE Web Agile freelance
    Rédacteur pour Developpez
    Professeur de Génie Logiciel à l'ESIEA

    Site : http://www.icauda.com / Linked'in : http://www.linkedin.com/in/thierryler / Twitter : @ThierryLeriche

  8. #8
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par thierryler Voir le message
    Je n'ai pas bien compris cette partie. D'abord, je dois créer un jar avec juste mes annotations + processeur ?
    Et le fichier "META-INF/services/javax.annotation.processing.Processor"


    C'est la notion de service : http://docs.oracle.com/javase/8/docs...iceLoader.html

    Grosso-modo dans le jar tu crées un répertoire "META-INF/services/" dans lequel tu crées un fichier du nom d'une interface (ici javax.annotation.processing.Processor).
    Ce fichier est destiné à recevoir le nom d'une (ou plusieurs) classe implémentant cette interface.

    Cela permet de récupérer ces implémentations via une simple ligne de code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for (Processor p : ServiceLoader.load(Processor.class)) {
       ...
    }
    Le compilateur javac s'en servira alors pour récupérer automatiquement les Processors défini dans le classpath lors de la compilation.
    En gros il suffit que le jar soit dans le classpath de compilation pour que le Processor soit utilisé...


    Citation Envoyé par thierryler Voir le message
    Et comment lancer depuis eclipse ?
    eclipse est super chiant là dessus (à moins que cela n'ait changé depuis le temps), car il faut activer le traitement des annotations et luis préciser exactement le jar.
    Dans les options du projet, section du compilateur Java il y a une page sur les annotations.

    Mais je ne me souviens plus trop ce qu'il faut faire précisément...


    a++

  9. #9
    Rédacteur
    Avatar de thierryler
    Homme Profil pro
    Inscrit en
    Octobre 2007
    Messages
    4 078
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 078
    Points : 12 815
    Points
    12 815
    Par défaut
    Ah génial.

    Merci à tous.
    Thierry Leriche-Dessirier
    Consultant Java JEE Web Agile freelance
    Rédacteur pour Developpez
    Professeur de Génie Logiciel à l'ESIEA

    Site : http://www.icauda.com / Linked'in : http://www.linkedin.com/in/thierryler / Twitter : @ThierryLeriche

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. générer un fichier xml à partir d'un gros fichier plat
    Par ybennani dans le forum Format d'échange (XML, JSON...)
    Réponses: 7
    Dernier message: 16/05/2007, 11h47
  2. [PL/SQL] générer un fichier xml à partir du programme pl/sql
    Par moneyinthebank dans le forum PL/SQL
    Réponses: 2
    Dernier message: 15/06/2006, 18h09
  3. générer un fichier XML à partir d'un prog en C
    Par filou18fr dans le forum XML/XSL et SOAP
    Réponses: 10
    Dernier message: 22/02/2006, 17h09
  4. générer un fichier xml à partir xsl ou xslt
    Par sarah1 dans le forum XSL/XSLT/XPATH
    Réponses: 3
    Dernier message: 17/05/2005, 18h57
  5. Réponses: 2
    Dernier message: 27/05/2004, 01h40

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo