55 votes

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

J'utilise actuellement JAXB pour générer des classes Java afin de démarchander le XML. J'aimerais maintenant créer un nouveau schéma très similaire au premier et faire en sorte que les classes générées mettent en œuvre la même interface.

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

adulte.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>

enfant.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, je voudrais 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 Person définit le getName() y getAge() méthodes.

J'ai regardé certaines des documentation pour le mappage des interfaces, mais il semble que ce soit uniquement pour la situation où vous avez déjà des classes Java que vous voulez mapper sur un DOM.

Aussi, 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 avec JAXB une classe qui implémente une interface ?

Mise à jour

J'ai essayé d'utiliser le Insertion d'interface mais pour une raison quelconque, je n'arrive pas à le faire fonctionner. Voici comment j'appelle xjc, mais c'est comme si le 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 ?

0 votes

Le plugin Interface Insertion peut être utilisé pour qu'une classe générée à partir d'un élément XSD implémente une certaine interface. Ici, vous voulez générer une classe basée sur son nom de schéma - vous aurez besoin d'un autre plugin pour le faire. J'ai réussi à faire fonctionner l'insertion d'interface avec maven. Faites-moi savoir si vous avez besoin des détails. Les sources de l'insertion d'interface sont ici : jaxb2-commons.dev.java.net/interface-insertion/

0 votes

<xjc:superInterface> est la voie à suivre.

53voto

Jim Hurne Points 2584

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

Heureusement, le Plugins de base de JAXB2 fournit un mécanisme similaire pour l'ajout d'une interface aux stubs JAXB générés (voir le fichier Plugin sur l'héritage ).

La documentation du plugin Inheritance contient un exemple montrant à quoi peut ressembler le fichier de schéma XML. Cependant, comme vous ne pouvez pas modifier le schéma, vous pouvez utiliser un fichier de liaisons externe à 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 comprend 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 désordonnée (en raison du nombre de jars que vous devez ajouter au classpath) :

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 de base de JAXB2 fournissent un certain nombre d'autres utilitaires que vous pourriez également trouver utiles (tels que la génération automatique des méthodes equals, hashCode et toString).

0 votes

Votre exemple tire parti d'une extension ou d'une capacité de base de l'interface utilisateur. maven-jaxb2-plugin ... existe-t-il une extension similaire pour cxf-xjc-plugin qui nous permettra de configurer quelque chose comme -Xinheritance ?

1 votes

J'ai découvert que cxf-xjc-plugin peut aussi profiter d'une configuration similaire si elle est modifiée comme ceci : gist.github.com/pulkitsinghal/8163296

1 votes

De même, le XPATH correct pour l'exemple donné devrait être //xs:element[@name='Person']/xs:complexType[1].

8voto

Chris Points 374

C'est peut-être un peu exagéré pour votre situation, mais j'ai fait cela en utilisant AspectJ (nous utilisions déjà Aspect sur ce projet, donc nous avions 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 ajoutera l'interface com.foo.Person aux classes com.foo.generated.Adult y com.foo.generated.Kid

C'est peut-être un peu exagéré pour vous, mais ça a marché pour nous.

0 votes

Je suppose que cela ne fonctionne qu'avec le tissage en temps de compilation, sinon les marshallers se plaignent de l'ajout de ajc$instance propriétés.

1voto

cfsh Points 11

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, l'erreur persiste lors de l'exécution de la tâche xjc ant. Le message d'erreur donné est irritant car la vraie raison dans mon cas est un mauvais numéro de version dans un fichier de classe que ant essaie de charger (voir stacktrace ci-dessous). Ce message d'erreur correct n'apparaît que lorsque vous ajoutez ce qui suit à 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

0voto

Thimmayya Points 1118

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

[ Pour appeler xjc avec le plugin d'insertion d'interface depuis 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 bien posté ce message en tant que commentaire, mais je n'ai pas assez de points pour commenter.

-3voto

skaffman Points 197885

Un plygin XJC d'une certaine description est la réponse à votre problème, il s'agit juste d'en trouver un qui fonctionne. La meilleure source pour cela est ici :

https://jaxb2-commons.dev.java.net/

En particulier, celui-ci :

https://jaxb2-commons.dev.java.net/interface-insertion/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X