2009-08-13 35 views
47

Actualmente estoy usando JAXB para generar clases Java para destrabar XML. Ahora me gustaría crear un nuevo esquema muy similar al primero y hacer que las clases que se generan implementen la misma interfaz.Generación de una clase JAXB que implementa una interfaz

decir, por ejemplo, tengo dos archivos de esquema XML que definen con etiquetas similares:

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> 

Usando JAXB y XJC I' Me gusta generar dos archivos de clase:

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 { ... } 
} 

donde la interfaz de persona define los métodos getName() y getAge().

He examinado algunas de las documentation para las interfaces de mapeo, pero esto parece ser solo para la situación en la que ya tiene clases Java que desea asignar a un DOM.

Además, he intentado usar este external plugin pero parece que no funciona.

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

pero esto da el siguiente error::

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

¿Es posible generar una clase con JAXB que implementa una interfaz Aquí está mi xjb archivo de enlace?

actualización

He intentado usar el plugin Interface Insertion pero por alguna razón no puede conseguir que funcione. Así es como yo estoy llamando xjc sin embargo, es como si el frasco plugin no está para ser recogido de la ruta de clases:

$ 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 

me sale el error:

unrecognized parameter -Xifins 

¿Alguna idea?

+0

El complemento de inserción de interfaz se puede utilizar para hacer que una clase generada a partir de un elemento XSD implemente una determinada interfaz. Aquí quiere generar una clase basada en su nombre de esquema; necesitará otro complemento para hacerlo. He logrado que la inserción de la interfaz funcione con maven. Avísame si necesitas los detalles. Las fuentes de la inserción de la interfaz están aquí: https://jaxb2-commons.dev.java.net/interface-insertion/xjc-if-ins-src.jar – rochb

+0

es el camino a seguir. – rochb

Respuesta

43

Por desgracia, parece que el plugin interfaz de inyección se menciona en algunas de las otras respuestas ya no está bien soportado. De hecho, tengo problemas para encontrar el archivo JAR para descargar.

Afortunadamente, el JAXB2 Basics Plugins proporciona un mecanismo similar para agregar una interfaz a los talones JAXB generados (consulte Inheritance plugin).

La documentación del complemento Herencia tiene un ejemplo que muestra cómo se vería el archivo de esquema XML.Sin embargo, ya no se puede modificar el esquema, puede utilizar presentar una fijaciones externas en su lugar:

<?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 documentación JAXB2 Fundamentos plugins incluye instrucciones para utilizar el plugin con Ant y Maven. También se puede utilizar directamente desde la línea de comandos, pero el comando es un desorden poco (debido a la cantidad de frascos que hay que añadir a la ruta de clase):

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 

El JAXB2 Fundamentos plugins proporciona un número de otra utilidades que también puede encontrar útiles (como la autogeneración de los métodos equals, hashCode y toString).

+0

Su ejemplo aprovecha una extensión o capacidad central del 'maven-jaxb2-plugin' ...¿hay una extensión similar para 'cxf-xjc-plugin' que nos permita configurar algo como' -Xinheritance'? – pulkitsinghal

+1

Descubrí que 'cxf-xjc-plugin' también puede aprovechar la configuración similar si se ajusta de esta manera: https://gist.github.com/pulkitsinghal/8163296 – pulkitsinghal

+0

Además, la XPATH correcta para el ejemplo dado debería ser // xs: element [@ name = 'Person']/xs: complexType [1] – RoyB

0

La documentación para el complemento de interfaz de inserción sugiere la siguiente

[ Para llamar a xjc con el complemento de inserción de interfaz desde la línea de comando, puede escribir:

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

]

que supongo que está llamando el método principal de la clase equivocada - com.sun.tools.xjc.XJCFacade. Probablemente deberías volver a intentar con la sintaxis exacta.

Aquí hay un enlace en otro foro que trata un problema similar. http://forums.java.net/jive/message.jspa?messageID=220686

  • Me hubiera publicado esto como un comentario, pero no tengo suficientes puntos para comentar.
8

Puede ser exagerado para su situación, pero he hecho esto usando AspectJ (ya estábamos usando aspectos en ese proyecto, por lo que ya teníamos la dependencia y la exposición).

Se podría declarar un aspecto a lo largo de las líneas de:

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

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

que se sumarán a la interfaz com.foo.Person las clases com.foo.generated.Adult y com.foo.generated.Adult

podría ser excesiva para su propósito, pero funcionó para nosotros .

+0

Supongo que esto solo funciona con el tiempo de compilación, de lo contrario los marshallers se quejan de las propiedades extra 'ajc $ instance'. – OrangeDog

0

¿Qué sucede si extraes el tipo en un XSD común importado en adult.xsd y kid.xsd? ¿O tiene una interfaz existente que necesita que implemente?

+0

Desgraciadamente, no puedo modificar los XSD. Espero hacer esto solo con un enlace o complemento JAXB para que no se necesiten cambios en los XSD. –

1

En mi caso la llamada de línea de comandos a través de java-jar funciona:

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

Sin embargo, cuando la ejecución de la xjc hormiguero tarea sigue siendo el error. El mensaje de error es irritante ya que la verdadera razón en mi caso es un número de versión malo en un archivo de clase que la hormiga intenta cargar (ver stacktrace a continuación). Este mensaje de error correcto sólo aparece cuando se agrega lo siguiente a 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 
0

Para quienes deseen ayuda en la creación de archivos de enlace de datos JAXB para esquemas más complejos, la última herramienta XML de código abierto, CAM Editor v2.2 ahora es compatible con esto.

Puede acceder a los recursos y descargar el sitio para la guía paso a paso y ver la sección de descargas en la parte superior de la página para la herramienta.

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

disfrutar.

3

La respuesta que funcionó para mí fue el ejemplo de Jim Hurne sobre el uso del complemento JAXB2 Basics. Pero la documentación que vincula parece ser ya no está disponible de modo de referencia, así es como he configurado el plugin de 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> 
+0

esto funcionó perfectamente. Sin embargo, las versiones son bastante viejas, y no pude obtener la misma solución para trabajar con las "últimas" versiones – Sean

3

Es posible lograr esto para este caso simple sin utilizar un plugin tercera Parte que aplica el JAXB RI Vendor Extensions xjc: personalización superInterface. Cabe señalar que este enfoque hará que todas las clases generadas implementen la interfaz. presentar

Ejemplo fijaciones:

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

A continuación, sólo tiene que ejecutar xjc especificando el archivo de unión y el establecimiento del indicador -Ampliación. ¡Mucho más rápido/más simple que traer JAXB2Basics!

Yo era escéptico de que esto funcionaría como los estados de documentación:

The customization allows you to specify the fully qualified name of the Java interface that is to be used as the root interface of all the generated interfaces. This customization has no effect unless you are specifically generating interfaces with the globalBindings generateValueClass="false" switch.

Pero cuando lo probé con fijaciones similares a la anterior (sin especificar generateValueClass = "falso", y la generación de clases , no interfaces), me dio la salida que requería - todas mis clases generadas implementaron la interfaz común.

+2

esto no es lo que la persona preguntó (y por lo general, lo que la mayoría de la gente quiere) como, como dijiste, esto haría que TODAS las clases implementaran la misma interfaz que es, la mayoría de las veces, inútil. –

+1

No, esto es exactamente lo que OP quería: dos esquemas que generan dos clases que implementan la misma interfaz. También sucedió que era un requisito que tenía en un proyecto del mundo real, por lo que algunas veces Es inútil en absoluto. Es un área bastante poco documentada de JAXB, no creo que poner un ejemplo de su uso sea un problema. –

+1

Si JAXB genera más de dos clases, agregando un bi global La sección nding usando dará como resultado TODAS las clases que implementan la interfaz Person, lo que no parece ser razonable para un dominio que consta de más de dos clases donde no todos los objetos son personas. –

Cuestiones relacionadas