Generación de clases Jaxb (XJC) a partir del esquema XML (XSD) con descripciones de clase y campo en forma de anotaciones. Complemento XJC

Creo que muchos desarrolladores de Java que al menos una vez se encontraron con Web- utilizaron la generación de clases Java DTO como se describe en el XML Schema ( XSD ) . Jaxb hace frente a esto con una explosión, no importa cómo usarlo, a través de wsimport o wsimport call desde la línea de comandos, maven o complementos de gradle.


Es muy rápido y fácil generar clases a partir de un esquema XSD . Pero aquí hay un problema: ¡las descripciones disponibles en el circuito original desaparecen casi por completo!


En la práctica , dado que solo la clase en sí tendrá una descripción Javadoc , en un formato fijo (donde no se puede separar la descripción y el fragmento XML sin los regulares, por ejemplo), la descripción de los campos (campos) está completamente ausente. Y si usted, como yo, los necesita también en tiempo de ejecución ( runtime ), entonces hay absolutamente problemas .


Por extraño que parezca, fue necesario superar esto, la tarea tomó mucho tiempo y, como resultado, escribí un complemento, que me gustaría presentar con la esperanza de que pueda salvar a alguien algunas horas en el futuro.


Características de Jaxb de un vistazo


Jaxb tiene una larga historia y una buena descripción oficial, que incluye agregar comportamiento a las clases generadas.


La herramienta principal para invocar la generación de clases desde la línea de comandos: xjc tampoco tiene el menor número de claves. Sin embargo, ninguno de ellos es para nuestro caso.


Por supuesto, no puede evitar mencionar -b , donde puede proporcionar su propio enlace. Y esta es un arma muy poderosa, especialmente en combinación con múltiples complementos. Una muy buena publicación de blog (en inglés) . Recomiendo leerlo como una breve introducción. Pero en su mayor parte, el enlace se limita a los valores estáticos de las antotaciones asignadas o los nombres de pila, lo que indica los elementos a los que se aplica. En mi problema esto no ayuda.


Complementos Jaxb (XJC)


Mientras buscaba una solución lista para usar, encontré muchos complementos que expanden la generación. Supongo que su revisión está más allá del alcance de esta publicación. Lo que es importante, no encuentro lo que está haciendo exactamente lo que necesito.


Pero luego, mientras leía las respuestas y preguntas en http://stackoverflow.com/ , encontré varias preguntas de este tipo, por ejemplo:


  1. Usando JAXB para manejar anotaciones de esquema .
  2. Cómo hacer que las clases generadas contengan Javadoc a partir de la documentación del esquema XML
  3. ¿Cómo puedo generar una clase de la que puedo recuperar el XML de un nodo como una cadena?

¡y ni una sola respuesta completa, en algunos durante muchos años!


Pero, volviendo al tema de las oportunidades, ¡resultó que hay una API para escribir complementos! Bajo nivel, a veces confuso, casi sin documentación ... Pero puedo decir que esto es muy avanzado, es decir, puede intervenir directamente en muchos procesos. Por cierto, a menudo se hace referencia a las respuestas, para muchos casos no estándar. Bueno, intenté escribir un complemento.


Para aquellos que estén interesados ​​en los detalles del proceso de escritura de sus complementos, puedo recomendar artículos:



Lo que quería y por qué


Para una de nuestras integraciones, el cliente proporcionó un archivo con archivos XSD , según el cual tuvimos que generar un modelo en MDM Unidata , con el que trabajamos, para configurar aún más las reglas de calidad.


Observo que el sistema MDM sí es propietario, es poco probable que muchos estén familiarizados con él, pero esto realmente no importa. La conclusión es que tuvimos que crear directorios y registros a partir de descripciones XSD . Al mismo tiempo, los registros tienen campos que usan tanto un identificador (nombre de campo) como un "nombre para mostrar", un nombre que es comprensible para una persona en ruso. Se puede dibujar una analogía simple (y este es probablemente también un ejemplo útil para la aplicación) con un RDBMS en términos del cual podemos suponer que queríamos crear tablas de base de datos, pero al mismo tiempo dar descripciones ( comment ) para cada columna para su uso posterior.

Mi plan era este:


  1. Generando clases XSD usando XJC
  2. Luego tomamos una hermosa biblioteca de reflejos e iteramos sobre todos los objetos, bajando recursivamente los campos.

Todo funcionó rápidamente para los nombres latinos, que fueron tomados de las clases de field . ¡Pero la descripción rusa, legible para los humanos, no estaba a ningún lado!


La edición manual de descripciones no es una opción, porque hay decenas de miles de campos anidados.


El primer intento fue escribir un analizador similar en Groovy , arrancando las descripciones de XSD . Y en general, se implementó. Pero rápidamente se hizo evidente que hay muchos casos en los que se requiere un procesamiento adicional: llamadas recursivas, soporte para extensiones XSD 1.1 en forma de restriction / extension (con soporte para herencia y anulación), diferentes tipos de los cuales generan campos de clase como <element> y <attribute> , <sequence> , <choose> y muchas otras pequeñas cosas. La implementación estaba cubierta de adiciones, pero ella no aumentó la armonía.


Como resultado, volví a la idea de escribir el complemento xjc-documentation-annotation-plugin , que les presento, ¡con la esperanza de que sea útil para alguien que no sea yo!


xjc-documentation-annotation-plugin


Todo se presenta en el github: https://github.com/Hubbitus/xjc-documentation-annotation-plugin
Hay instrucciones, pruebas y un - gradle separado - gradle con un ejemplo de uso.


Creo que no tiene sentido copiar la descripción aquí desde allí, solo mostrar brevemente lo que hace.


Digamos que hay un fragmento de XSD :


  <xs:complexType name="Customer"> <xs:annotation> <xs:documentation></xs:documentation> </xs:annotation> <xs:sequence> <xs:element name="name" type="xs:string"> <xs:annotation> <xs:documentation>  </xs:documentation> </xs:annotation> </xs:element> </xs:sequence> </xs:complexType> 

Por defecto, XJC generará una clase de XJC partir de él (getters, setters y algunos detalles irrelevantes se omiten para facilitar la lectura):


 /** *  * * <p>Java class for Customer complex type. * * <p>The following schema fragment specifies the expected content contained within this class. * * <pre> * &lt;complexType name="Customer"&gt; * &lt;complexContent&gt; * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt; * &lt;sequence&gt; * &lt;element name="name" type="{http://www.w3.org/2001/XMLSchema}string"/&gt; * &lt;element name="age" type="{http://www.w3.org/2001/XMLSchema}positiveInteger"/&gt; * &lt;/sequence&gt; * &lt;/restriction&gt; * &lt;/complexContent&gt; * &lt;/complexType&gt; * </pre> * * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "Customer", propOrder = { "name", "age" }) public class Customer { @XmlElement(required = true) protected String name; @XmlElement(required = true) @XmlSchemaType(name = "positiveInteger") protected BigInteger age; } 

Con el complemento, obtienes ( Javadoc sigue siendo el mismo, omitido por simplicidad):


 @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "Customer", propOrder = { "name", "age" }) @XsdInfo(name = "", xsdElementPart = "<complexType name=\"Customer\">\n <complexContent>\n <restriction base=\"{http://www.w3.org/2001/XMLSchema}anyType\">\n <sequence>\n <element name=\"name\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>\n <element name=\"age\" type=\"{http://www.w3.org/2001/XMLSchema}positiveInteger\"/>\n </sequence>\n </restriction>\n </complexContent>\n</complexType>") public class Customer { @XmlElement(required = true) @XsdInfo(name = "  ") protected String name; @XmlElement(required = true) @XmlSchemaType(name = "positiveInteger") @XsdInfo(name = "") protected BigInteger age; } 

Echa un vistazo a las anotaciones @XmlType agregadas.


Usar esto es tan simple como cualquier otra anotación:


  XsdInfo xsdAnnotation = CadastralBlock.class.getDeclaredAnnotation(XsdInfo.class); System.out.println("XSD description: " + xsdAnnotation.name()); 

Un ejemplo de trabajo está en las pruebas: 1 , 2 .

Source: https://habr.com/ru/post/437914/


All Articles