Jaxb (XJC) class generation from XML Schema (XSD) with annotation class and field descriptions. Xjc plugin

    I think many Java-developers who at least once came across Web-сервисами, used the generation of JavaDTOclasses by description XML Schema( XSD) . Jaxb copes with this with a bang, it does not matter how to use it, through xjceither wsimporta command line call, maven or gradle plugins.


    So quickly and easily generate classes from the XSDschema. But here is one problem - the descriptions in the original scheme are almost completely lost!


    Practically , because the Javadocdescription will be only for the class itself, in a fixed format (where we will not separate the description and the fragment XMLwithout regulars, say), the description of the fields (fields) is completely absent. And if you need them, like me, you also need them at runtime ( runtime) - this is a real disaster .


    It was with this that I had to fight, oddly enough, the task took a lot of time, and as a result I wrote a plugin, which I would like to present in the hope that he could save someone a few hours in the future.


    Jaxb features overview


    Jaxb has a great history and a good official description, including the addition of behavior to the generated classes.


    The main tool for invoking class generation from the command line - xjc also has not the smallest number of keys. But none of them, not for our case.


    Of course it is impossible not to mention about -bwhere you can provide your binding. And this is a very powerful weapon, especially in conjunction with multiple plug-ins. Very good blog post (in English) - I recommend reading as a brief introduction. But the binding is mostly limited to the static values ​​of the assigned annotations or specified names, indicating the elements to which it is applied. It doesn't help my problem.


    Jaxb (XJC) Plugins


    While looking for a ready-made solution, I found many plug-ins that extend generation. I believe their review is beyond the scope of this post. What is important, doing what is necessary to me, I have not found.


    But then, while reading the answers and questions at http://stackoverflow.com/ , I found several questions of this kind, for example:


    1. Using JAXB to handle schema annotations .
    2. How to make generated classes contain Javadoc from XML Schema documentation
    3. How can I generate a XML from a node as a String

    and not a single complete answer, some over many years!


    But, returning to the topic of possibilities, it turned out that there is APIfor writing plugins! Low-level, sometimes confusing, almost without documentation ... But I can say with this very advanced, that is, you can directly intervene in many processes. By the way, the responses to it are often referenced, for many non-standard cases. So I tried to write a plugin.


    For those who are interested in the details of the process of writing their plug-ins, I can recommend articles:



    What you wanted and why


    For one of our integrations, the customer provided an archive with the XSDfiles for which we had to generate a model in MDM Unidata , with which we work, to further adjust the quality rules.


    I will note that the MDMsystem itself is proprietary, it is unlikely that many are familiar with it, but this does not matter much. The bottom line is that we had to create XSDreference books and registries from the descriptions. At the same time, the registries have fields in which both the identifier (the name of the field) and the "display name" are used - the name that the person understands is in Russian. A simple analogy can be drawn (and this is probably also a useful example for use) with RDBMS in terms of which we can assume that we wanted to make database tables, but at the same time give descriptions ( comment) for each column for later use.

    My plan was:


    1. We generate classes by XSDusingXJC
    2. Then we take an excellent library of reflections and iterate over all objects, recursively descending through the fields.

    Everything worked quickly for Latin names that were taken from fields ( field) classes. But the Russian, humane description was nowhere to take!


    Manual description is not an option, because there are tens of thousands of nested fields.


    The first attempt was to write a similar parser on Groovyits own, ripping out descriptions from XSD. And in general, it was implemented. But it soon became clear that there are many cases which require further processing - recursive calls, support for extensions XSD1.1 in the form of restriction/ extension(with support for inheritance and override), various types of which are generated by a field classes like <element>and <attribute>, <sequence>, <choose>and many other details. Realization overgrown with additions, but she was not slim.


    As a result, I returned to the idea of ​​writing the xjc-documentation-annotation-plugin plugin , which I represent to you, in the hope that it will be useful to someone other than me!


    xjc-documentation-annotation-plugin


    Everything is laid out on the githaba: https://github.com/Hubbitus/xjc-documentation-annotation-plugin
    There are also instructions, tests and a separate демо-проект для gradleexample of use.


    I think it makes no sense to copy the description here from there, just briefly show you what he does.


    Let's say there is such a XSDfragment:


    <xs:complexTypename="Customer"><xs:annotation><xs:documentation>Пользователь</xs:documentation></xs:annotation><xs:sequence><xs:elementname="name"type="xs:string"><xs:annotation><xs:documentation>Фамилия и имя</xs:documentation></xs:annotation></xs:element></xs:sequence></xs:complexType>

    By default, XJCa class of the type will be generated from it (getters, setters, and some of the irrelevant details are omitted for readability):


    /**
     * Пользователь
     * 
     * <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"
    })
    publicclassCustomer{
        @XmlElement(required = true)
        protected String name;
        @XmlElement(required = true)
        @XmlSchemaType(name = "positiveInteger")
        protected BigInteger age;
    }

    With the plugin, you will receive (it Javadocremains the same, omitted for simplicity):


    @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>")
    publicclassCustomer{
        @XmlElement(required = true)
        @XsdInfo(name = "Фамилия и имя")
        protected String name;
        @XmlElement(required = true)
        @XmlSchemaType(name = "positiveInteger")
        @XsdInfo(name = "Возраст")
        protected BigInteger age;
    }

    Notice the added annotations @XmlType.


    Using it is then as simple as any other annotation:


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

    A working example is in the tests: 1 , 2 .


    Also popular now: