web-dev-qa-db-fra.com

Générer une classe JAXB qui implémente une interface

J'utilise actuellement JAXB pour générer des classes Java afin de supprimer le XML. Maintenant, j'aimerais créer un nouveau schéma très similaire au premier et faire en sorte que les classes générées implémentent la même interface.

Par exemple, j'ai deux fichiers de schéma qui définissent XML avec des balises similaires:

adult.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:element name="Person">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Name" type="xs:string" />
      <xs:element name="Age" type="xs:integer" />
      <xs:element name="Job" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

kid.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:element name="Person">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Name" type="xs:string" />
      <xs:element name="Age" type="xs:integer" />
      <xs:element name="School" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

En utilisant JAXB et XJC, j'aimerais générer deux fichiers de classe:

public class Adult implements Person {
    ...
    public String getName() { ... }
    public int getAge() { ... }
    public String getJob { ... }
}

public class Kid implements Person {
    ...
    public String getName() { ... }
    public int getAge() { ... }
    public String getSchool { ... }
}

où l'interface Personne définit les méthodes getName() et getAge().

J'ai examiné certaines des documentation pour les interfaces de mappage, mais cela ne concerne que le cas où vous avez déjà des classes Java que vous souhaitez mapper à un DOM.

De plus, j'ai essayé d'utiliser ce plugin externe mais cela ne semble pas fonctionner. Voici mon fichier de liaison xjb:

<jxb:bindings version="1.0" 
  xmlns:jxb="http://Java.Sun.com/xml/ns/jaxb" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:xjc="http://Java.Sun.com/xml/ns/jaxb/xjc"
  xmlns:ext="http://xml.w-wins.com/xjc-plugins/interfaces"
  jxb:extensionBindingPrefixes="xjc">

    <jxb:bindings schemaLocation="xsd/adult.xsd" node="xs:schema/xs:complexType[@name='Person']">
        <ext:interface>mypackage.Hello</ext:interface> 
    </jxb:bindings>

</jxb:bindings>

mais cela donne l'erreur suivante:

$ Java -cp "lib/activation.jar;lib/InterfacesXJCPlugin.jar;lib/jaxb1-impl.jar;lib/jaxb-api.jar;lib/jaxb-xjc.jar;lib/jsr173_1.0_api.jar" com.Sun.tools.xjc.XJCFacade -p mypackage.myxml -extension -Xinterfaces xsd/adult.xsd -b binding.xjb
parsing a schema...
[ERROR] XPath evaluation of "xs:schema/xs:complexType[@name='Person']" results in empty target node
  line 8 of file:/C:/dev/jaxb/jaxb-ri/binding.xjb

Failed to parse a schema.

Est-il possible de générer une classe avec JAXB qui implémente une interface?

Mettre à jour

J'ai essayé d'utiliser le plugin Interface Insertion mais je ne peux pas le faire fonctionner pour une raison quelconque. C’est ainsi que j’appelle xjc mais c’est comme si le fichier jar du plugin n’était pas récupéré dans le classpath:

$ Java -cp "lib/xjc-if-ins.jar;lib/jaxb-xjc.jar" com.Sun.tools.xjc.XJCFacade -p mypackage -extension -Xifins myschema.xsd -b binding.xjb

Je reçois l'erreur:

unrecognized parameter -Xifins

Des idées?

48
Alex Spurling

Malheureusement, il semble que le plug-in d'interface-interface mentionné dans certaines des autres réponses ne soit plus bien supporté. En fait, j'ai du mal à trouver le fichier JAR à télécharger.

Heureusement, le JAXB2 Basics Plugins fournit un mécanisme similaire pour ajouter une interface aux stubs JAXB générés (voir le plugin Héritage ). 

La documentation du plug-in Inheritance contient un exemple montrant à quoi le fichier de schéma XML pourrait ressembler. Cependant, comme vous ne pouvez pas modifier le schéma, vous pouvez utiliser un fichier de liaisons externes à la place:

<?xml version="1.0"?>
<jxb:bindings version="1.0" 
  xmlns:jxb="http://Java.Sun.com/xml/ns/jaxb" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:xjc="http://Java.Sun.com/xml/ns/jaxb/xjc"
  xmlns:inheritance="http://jaxb2-commons.dev.Java.net/basic/inheritance"
  jxb:extensionBindingPrefixes="xjc">

    <jxb:bindings schemaLocation="xsd/adult.xsd">
      <jxb:bindings node="//xs:complexType[@name='Person']">
        <inheritance:implements>mypackage.Hello</inheritance:implements> 
      </jxb:bindings>
    </jxb:bindings>

</jxb:bindings>

La documentation de JAXB2 Basics Plugins contient des instructions pour l’utilisation du plugin avec Ant et Maven. Vous pouvez également l'utiliser directement à partir de la ligne de commande, mais la commande est un peu compliquée (en raison du nombre de fichiers jar que vous devez ajouter au chemin de classe):

Java -jar jaxb-xjc.jar 
     -classpath jaxb2-basics-0.5.3.jar,jaxb2-basics-runtime-0.5.3.jar,
                jaxb2-basics-tools-0.5.3.jar,commons-beanutils-0.5.3.jar,
                commons-lang.jar,commons-logging.jar
     -p mypackage.myxml -extension -Xinheritance xsd/adult.xsd -b binding.xjb

Les plugins JAXB2 Basics fournissent un certain nombre d’utilitaires qui pourraient également vous être utiles (tels que la génération automatique d’égal, les méthodes hashCode et toString).

43
Jim Hurne

C'est peut-être excessif pour votre situation, mais je l'ai fait avec AspectJ (nous utilisions déjà des aspects de ce projet, nous avions donc déjà la dépendance et l'exposition).

Vous déclareriez un aspect du type:

public aspect MyAspect
{
    declare parents: 
        com.foo.generated.Adult
    implements com.foo.Person;

    declare parents: 
        com.foo.generated.Kid
    implements com.foo.Person;
}

Ce qui va ajouter l'interface com.foo.Person aux classes com.foo.generated.Adult et com.foo.generated.Adult

Peut-être exagéré pour votre but, mais cela a fonctionné pour nous.

8
Chris

La réponse qui a fonctionné pour moi est l'exemple de Jim Hurne qui utilise le plugin JAXB2 Basics. Mais la documentation qu'il a liée semble ne plus être disponible, alors voici comment j'ai configuré le plugin maven:

        <plugin>
            <groupId>org.jvnet.jaxb2.maven2</groupId>
            <artifactId>maven-jaxb2-plugin</artifactId>
            <version>0.8.2</version>
            <executions>
                <execution>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                    </execution>
            </executions>
            <configuration>
                <extension>true</extension>
                <args>
                    <arg>-Xinheritance</arg>
                </args>
                <bindingDirectory>src/main/resources/xjb</bindingDirectory>
                <bindingIncludes>
                    <include>**.xml</include> <!-- This Should reference the binding files you use to configure the inheritance -->
                </bindingIncludes>
                <schemaDirectory>src/main/resources/xsd</schemaDirectory>
                <generateDirectory>${project.build.directory}/generated-sources/jaxb</generateDirectory>
                <generatePackage>mypackage</generatePackage>
                <plugins>
                    <plugin>
                        <groupId>org.jvnet.jaxb2_commons</groupId>
                        <artifactId>jaxb2-basics</artifactId>
                        <version>0.5.3</version>
                    </plugin>
                </plugins>
            </configuration>
        </plugin>
3
Alex Spurling

Il est possible d'y parvenir pour ce cas simple sans utiliser de plug-in tiers utilisant la personnalisation extensions du fournisseur JAXB RI xjc: superInterface. Il convient de noter que cette approche obligera toutes les classes générées à implémenter l'interface. 

Exemple de fichier de liaisons:

<jxb:bindings version="1.0" 
  xmlns:jxb="http://Java.Sun.com/xml/ns/jaxb" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:xjc="http://Java.Sun.com/xml/ns/jaxb/xjc" 
  jxb:extensionBindingPrefixes="xjc">
    <jxb:bindings schemaLocation="adult.xsd" node="/xs:schema">
    <jxb:globalBindings>
        <xjc:superInterface name="com.example.model.Person"/>
    </jxb:globalBindings>
    </jxb:bindings>
</jxb:bindings>

Il vous suffit ensuite d’exécuter xjc pour spécifier le fichier de liaison et définir l’indicateur -extension. Beaucoup plus rapide/simple que de faire appel à JAXB2Basics!

Au départ, j'étais sceptique sur le fait que cela fonctionnerait comme le dit la documentation:

La personnalisation vous permet de spécifier le nom complet de l'interface Java à utiliser en tant qu'interface racine de toutes les interfaces générées. Cette personnalisation n'a d'effet que si vous générez spécifiquement des interfaces avec le commutateur globalBindings generateValueClass = "false".

Mais lorsque je l'ai essayé avec une liaison similaire à celle ci-dessus (sans spécifier generateValueClass = "false", et en générant des classes, pas des interfaces), cela m'a donné la sortie requise - toutes mes classes générées implémentaient l'interface commune. 

3
Malcolm Smith

Dans mon cas, l’appel en ligne de commande via Java -jar fonctionne:

Java -jar $somepath/jaxb-xjc.jar -classpath $somepath/xjc-if-ins.jar my.xsd -d $destdir -b $bindingconfig -p $desiredpackage -extension -Xifins

Cependant, lors de l'exécution de la tâche ant-task xjc, l'erreur persiste. Le message d'erreur donné est irritant car la vraie raison dans mon cas est un numéro de version incorrect dans un fichier de classe qu'il essaie de charger (voir stacktrace ci-dessous). Ce message d'erreur correct apparaît uniquement lorsque vous ajoutez les éléments suivants à ANT_OPTS: -Dcom.Sun.tools.xjc.Options.findServices=true

[xjc] Java.lang.UnsupportedClassVersionError: Bad version number in .class file
[xjc]     at Java.lang.ClassLoader.defineClass1(Native Method)
[xjc]     at Java.lang.ClassLoader.defineClass(ClassLoader.Java:620)
[xjc]     at org.Apache.tools.ant.AntClassLoader.defineClassFromData(AntClassLoader.Java:1134)
[xjc]     at org.Apache.tools.ant.AntClassLoader.getClassFromStream(AntClassLoader.Java:1320)
[xjc]     at org.Apache.tools.ant.AntClassLoader.findClassInComponents(AntClassLoader.Java:1376)
[xjc]     at org.Apache.tools.ant.AntClassLoader.findClass(AntClassLoader.Java:1336)
[xjc]     at org.Apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.Java:1074)
[xjc]     at Java.lang.ClassLoader.loadClass(ClassLoader.Java:299)
[xjc]     at Java.lang.ClassLoader.loadClass(ClassLoader.Java:251)
[xjc]     at com.Sun.tools.xjc.Options.findServices(Options.Java:936)
[xjc]     at com.Sun.tools.xjc.Options.getAllPlugins(Options.Java:336)
[xjc]     at com.Sun.tools.xjc.Options.parseArgument(Options.Java:632)
[xjc]     at com.Sun.tools.xjc.Options.parseArguments(Options.Java:742)
[xjc]     at com.Sun.tools.xjc.XJC2Task._doXJC(XJC2Task.Java:444)
[xjc]     at com.Sun.tools.xjc.XJC2Task.doXJC(XJC2Task.Java:434)
[xjc]     at com.Sun.tools.xjc.XJC2Task.execute(XJC2Task.Java:369)
[xjc]     at com.Sun.istack.tools.ProtectedTask.execute(ProtectedTask.Java:55)
[xjc]     at org.Apache.tools.ant.UnknownElement.execute(UnknownElement.Java:291)
[xjc]     at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[xjc]     at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:39)
[xjc]     at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:25)
[xjc]     at Java.lang.reflect.Method.invoke(Method.Java:585)
[xjc]     at org.Apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.Java:106)
[xjc]     at org.Apache.tools.ant.Task.perform(Task.Java:348)
[xjc]     at org.Apache.tools.ant.Target.execute(Target.Java:390)
[xjc]     at org.Apache.tools.ant.Target.performTasks(Target.Java:411)
[xjc]     at org.Apache.tools.ant.Project.executeSortedTargets(Project.Java:1360)
[xjc]     at org.Apache.tools.ant.Project.executeTarget(Project.Java:1329)
[xjc]     at org.Apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.Java:41)
[xjc]     at org.Apache.tools.ant.Project.executeTargets(Project.Java:1212)
[xjc]     at org.Apache.tools.ant.Main.runBuild(Main.Java:801)
[xjc]     at org.Apache.tools.ant.Main.startAnt(Main.Java:218)
[xjc]     at org.Apache.tools.ant.launch.Launcher.run(Launcher.Java:280)
[xjc]     at org.Apache.tools.ant.launch.Launcher.main(Launcher.Java:109)
[xjc]
[xjc] failure in the XJC task. Use the Ant -verbose switch for more details
1
cfsh

La documentation du plugin interface-insertion suggère ce qui suit

[ Pour appeler xjc avec le plug-in d’insertion d’interface à partir de la ligne de commande, vous pouvez écrire:

Java -cp $JAXB_HOME/share/lib/xjc-if-ins.jar -extension -Xifins schema

]

Je suppose que vous appelez la méthode principale de la mauvaise classe, com.Sun.tools.xjc.XJCFacade. Vous devriez probablement réessayer avec la syntaxe exacte. 

Voici un lien sur un autre forum qui traite d'un problème similaire . http://forums.Java.net/jive/message.jspa?messageID=220686

  • J'aurais posté cela comme un commentaire, mais je n'ai pas assez de points pour commenter.
0
Thimmayya

Et si vous extrayez le type dans un XSD commun importé dans adult.xsd et kid.xsd? Ou avez-vous une interface existante dont vous avez besoin pour les implémenter?

0
Mirvnillith

Pour ceux qui souhaitent obtenir de l'aide pour créer des fichiers de liaison de données JAXB pour un schéma plus complexe - le dernier outil XML open source - CAM Editor v2.2 le prend désormais en charge.

Vous pouvez accéder aux ressources et au site de téléchargement pour le guide étape par étape et pour voir la section des téléchargements en haut de la page pour l'outil.

http://www.cameditor.org/#JAXB_Bindings

Prendre plaisir.

0
user841942