Je pense que de nombreux développeurs Java qui ont rencontré Web-
au moins une fois ont utilisé la génération de classes Java
DTO
comme décrit par XML Schema
( XSD
) . Jaxb fait face à cela avec un bang, peu importe comment l'utiliser, via xjc
ou wsimport
un appel en ligne de commande, des plugins maven ou gradle.
Il est si rapide et facile de générer des classes à partir d'un schéma XSD
. Mais voici un problème - les descriptions disponibles dans le circuit d'origine disparaissent presque complètement!
En pratique , parce que seule la classe elle-même aura une description Javadoc
, dans un format fixe (où vous ne pouvez pas séparer la description et le fragment XML
sans les habitués, par exemple), la description des champs (champs) est complètement absente. Et si vous, comme moi, en avez également besoin lors de l'exécution ( runtime
), alors il y a absolument des problèmes .
Aussi étrange que cela puisse paraître, il a fallu surmonter cela, la tâche a pris beaucoup de temps, et en conséquence j'ai écrit un plug-in, que je voudrais présenter dans l'espoir que cela puisse faire gagner quelques heures à certaines personnes à l'avenir.
Aperçu des fonctionnalités de Jaxb
Jaxb a une longue histoire et une bonne description officielle, y compris l'ajout de comportement aux classes générées.
L'outil principal pour invoquer la génération de classes à partir de la ligne de commande - xjc n'a pas non plus le plus petit nombre de clés. Cependant, aucun d'entre eux n'est pour notre cas.
Bien sûr, vous ne pouvez pas vous empêcher de mentionner -b
, où vous pouvez fournir votre propre liaison. Et c'est une arme très puissante, surtout en conjonction avec plusieurs plug-ins. Un très bon article de blog (en anglais) - je recommande de le lire comme une brève introduction. Mais la liaison est pour la plupart limitée aux valeurs statiques des anthotations ou des prénoms attribués, indiquant les éléments auxquels elle est appliquée. Dans mon problème, cela n'aide pas.
Plugins Jaxb (XJC)
Alors que je cherchais une solution toute faite, j'ai trouvé de nombreux plugins qui étendent la génération. Je suppose que leur critique dépasse le cadre de ce post. Ce qui est important, je ne trouve pas ce qui fait exactement ce dont j'ai besoin.
Mais ensuite, en lisant les réponses et les questions sur http://stackoverflow.com/ , j'ai trouvé plusieurs questions de ce type, par exemple:
- Utilisation de JAXB pour gérer les annotations de schéma .
- Comment faire pour que les classes générées contiennent Javadoc à partir de la documentation du schéma XML
- Comment puis-je générer une classe à partir de laquelle je peux récupérer le XML d'un nœud en tant que chaîne
et pas une seule réponse complète, dans certains sur plusieurs années!
Mais, revenant au sujet des opportunités, il s'est avéré qu'il existe une API
pour écrire des plugins! Bas niveau, parfois déroutant, presque sans documentation ... Mais je peux dire que c'est très avancé, c'est-à-dire que vous pouvez directement intervenir beaucoup dans quels processus. Soit dit en passant, les réponses y sont souvent citées, pour de nombreux cas non standard. Eh bien, j'ai essayé d'écrire un plugin.
Pour ceux qui sont intéressés par les détails du processus d'écriture de leurs plugins, je peux recommander des articles:
Ce que je voulais et pourquoi
Pour l'une de nos intégrations, le client a fourni une archive avec des fichiers XSD
, selon laquelle nous devions générer un modèle dans MDM Unidata , avec lequel nous travaillons, pour configurer davantage les règles de qualité.
Je note que le système MDM
lui-même est propriétaire, il est peu probable que beaucoup le connaissent, mais cela n'a pas vraiment d'importance. L'essentiel est que nous devions créer des répertoires et des registres à partir des descriptions XSD
. En même temps, les registres ont des champs qui utilisent à la fois un identifiant (nom de champ) et un "nom d'affichage" - un nom compréhensible pour une personne en russe. Une analogie simple peut être tracée (et c'est probablement aussi un exemple utile pour l'application) avec un SGBDR en fonction duquel nous pouvons supposer que nous voulions créer des tables de base de données, mais en même temps donner des descriptions ( comment
) pour chaque colonne pour une utilisation ultérieure.
Mon plan était le suivant:
- Génération de classes
XSD
aide de XJC
- Ensuite, nous prenons une belle bibliothèque de réflexions et parcourons tous les objets, en descendant récursivement dans les champs.
Tout a rapidement fonctionné pour les noms latins, qui ont été tirés des cours field
le field
. Mais la description russe lisible par l'homme n'était nulle part où aller!
La modification manuelle des descriptions n'est pas une option, car il existe des dizaines de milliers de champs imbriqués.
La première tentative a été d'écrire un analyseur similaire sur Groovy
vous-même, déchirant les descriptions de XSD
. Et en général, il a été mis en œuvre. Mais il est rapidement devenu évident qu'il existe de nombreux cas où un traitement supplémentaire est nécessaire - appels récursifs, prise en charge des extensions XSD
1.1 sous forme de restriction
/ extension
(avec prise en charge de l'héritage et de la substitution), dont différents types génèrent des champs de classe tels que <element>
et <attribute>
, <sequence>
, <choose>
et bien d'autres petites choses. La mise en œuvre a été envahie par des ajouts, mais elle n'a pas augmenté l'harmonie.
Du coup, je suis revenu à l'idée d'écrire le plugin xjc-documentation-annotation-plugin , que je vous présente, dans l'espoir qu'il sera utile à quelqu'un d'autre que moi!
xjc-documentation-annotation-plugin
Tout est disposé sur le github: https://github.com/Hubbitus/xjc-documentation-annotation-plugin
Il y a des instructions, des tests et un - gradle
séparé - gradle
avec un exemple d'utilisation.
Je pense que cela n'a aucun sens de copier la description ici à partir de là, juste montrer brièvement ce qu'il fait.
Disons qu'il existe un tel fragment 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>
Par défaut, XJC
générera une classe de XJC
(les getters, setters et certains détails non pertinents sont omis pour plus de lisibilité):
@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; }
Avec le plugin, vous obtenez ( Javadoc
reste le même, omis pour plus de simplicité):
@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; }
Consultez les annotations @XmlType
ajoutées.
Son utilisation est alors aussi simple que n'importe quelle autre annotation:
XsdInfo xsdAnnotation = CadastralBlock.class.getDeclaredAnnotation(XsdInfo.class); System.out.println("XSD description: " + xsdAnnotation.name());
Un exemple de travail est dans les tests: 1 , 2 .