Избранное сообщение

Фетісов В. С. Комп’ютерні технології в тестуванні. Навчально-методичний посібник. 2-ге видання, перероблене та доповнене / Мои публикации

В 10-х годах я принимал участие в программе Европейского Союза Tempus "Освітні вимірювання, адаптовані до стандартів ЄС". В рамк...

Благодаря Интернету количество писателей и поэтов увеличивается в геометрической прогрессии. Поголовье читателей начинает заметно отставать.

четверг, 31 января 2019 г.

Jaxb (XJC) генерация классов из XML Schema (XSD) с описаниями классов и полей в виде аннотаций. XJC плагин / Программирование на Java

Думаю многие Java-разработчики которые хоть раз сталкивались с Web-сервисами, использовали генерацию Java DTO классов по описанию XML Schema (XSD)Jaxb с этим справляется на ура, не важно как его использовать, через xjc или wsimport вызов из командной строки, maven или gradle плагины.
Так быстро и просто сгенерировать классы из XSD схемы. Но вот одна проблема — практически полностью пропадают описания, имевшиеся в исходной схеме!
Практически, потому что Javadoc описание будет только у самого класса, в фиксированном формате (где не разделить описание и фрагмент XML без регулярок скажем), описание полей (филдов) отсутствуют полностью. А если вам они, как мне, нужны ещё и во время выполнения (runtime) — тут совсем беда.
Именно с этим, пришлось побороться, как ни странно, задача заняла много времени, и в результате я написал плагин, который и хотел бы представить в надежде что он может кому-то сэкономить несколько часов в будущем.


Краткий обзор возможностей Jaxb



Jaxb имеет большую историю и неплохое официальное описание, в том числе по добавлению поведения в генерируемые классы.
Основной инструмент для вызова генерации классов из командной строки — xjc также имеет не самое маленькое количество ключей. Впрочем ни один из них, не для нашего случая.
Конечно нельзя не упомянуть про -b, где можно предоставить свой биндинг. И это весьма мощное оружие, особенно в купе с множественными плагинами. Весьма неплохой блог пост (на английском) — рекомендую к прочтению как краткое введение. Но биндинг в большинстве своём ограничен статическими значениями присваемых аннтотаций или задаваемых имён, с указанием элементов, к которым он применяется. В моей проблеме это не помогает.

Jaxb (XJC) плагины



Пока искал готовое решение, нашёл множество плагинов, которые расширяют генерацию. Полагаю их обзор выходит за рамки данного поста. Что важно, делающего то что нужно именно мне, я так и не нашёл.
Но зато, пока читал ответы и вопросы на http://stackoverflow.com/, нашёл несколько вопросов, подобного рода, например:
  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 class from which I can retrieve the XML of a node as a String


и ни одного полного ответа, в некоторых за много лет!
Зато, возвращаясь к теме возможностей, оказалось что имеется API для написания плагинов! Низкоуровневое, местами запутанное, почти без документации… Но могу сказать при это весьма продвинутое, то есть вмешаться можно прямо много в какие процессы. Кстати в ответах на него часто ссылаются, для многих нестандартных случаев. Ну я и попробовал написать плагин.
Для тех, кому интересны подробности процесса написания своих плагинов, могу порекомендовать статьи:


Что хотел и зачем



Для одной из наших интеграций заказчик предоставил архив с XSD файлами, по которым мы должны были сгенерировать модель в MDM Unidata, с которой мы работаем, для дальнейшей настройки правил качества.
Отмечу что сама MDM система проприетарная, вряд ли многие с ней знакомы, но это не имеет особого значения. Суть в том что мы должны были создать по XSD описаниям справочники и реестры. При этом у реестров есть поля, в которых используется как идентификатор (имя поля), так и "отображаемое имя" — название, понятное человеку, на русском. Простую аналогию можно провести (и это вероятно также полезный пример для применения) с РСУБД в терминах которой можно считать что мы хотели сделать таблицы БД, но при этом дать описания (comment) для каждого столбца для последующего использования.
Мой план был такой:
  1. Генерируем классы по XSD с помощью XJC
  2. Затем берём прекрасную библиотеку reflections и итерируем по всем объектам, рекурсивно спускаясь по полям.


Всё быстро заработало для латинских имён, которые брались из филдов (field) классов. А вот русское, человекопонятное описание было взять неоткуда!
Править описания вручную — не вариант, потому что вложенных полей десятки тысяч.
Первой попыткой было написать подобный парсер на Groovy самостоятельно, выдирая описания из XSD. И в общем он был реализован. Но быстро выяснилось что имеется множество случаев, где требуется дополнительная обработка — рекурсивные вызовы, поддержка расширений XSD1.1 в виде restriction/extension (с поддержкой наследования и переопределения), разные типы из которых генерируются поля классов вроде <element> и <attribute><sequence><choose> и много других мелочей. Реализация обрастала дополнениями, а стройности ей не прибавлялось.
В итоге я вернулся к мысли написать плагин xjc-documentation-annotation-plugin, который вам и представляю, в надежде что он будет кому-то полезен кроме меня!

xjc-documentation-annotation-plugin



Всё выложено на гитхабе: https://github.com/Hubbitus/xjc-documentation-annotation-plugin
Там же есть инструкции, тесты и отдельный демо-проект для gradle с примером использования.
Полагаю нет смысла копировать сюда описание оттуда, просто вкратце покажу что именно он делает.
Скажем есть такой XSD фрагмент:
    <xs:complexType name="CadastralBlock">
        <xs:annotation>
            <xs:documentation>Кадастровый квартал</xs:documentation>
        </xs:annotation>
        <xs:sequence>
            <xs:element name="number" type="xs:string">
                <xs:annotation>
                    <xs:documentation>Кадастровый номер</xs:documentation>
                </xs:annotation>
            </xs:element>
        </xs:sequence>
    </xs:complexType>


По умолчанию XJC сгенерит из него класс вида (геттеры, сеттеры и часть не имеющих к делу мелочей опущены для читаемости):
/**
 * Кадастровый квартал
 * 
 * <p>Java class for CadastralBlock complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="CadastralBlock"&gt;
 *   &lt;complexContent&gt;
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
 *       &lt;sequence&gt;
 *         &lt;element name="number" type="{http://www.w3.org/2001/XMLSchema}string"/&gt;
 *       &lt;/sequence&gt;
 *     &lt;/restriction&gt;
 *   &lt;/complexContent&gt;
 * &lt;/complexType&gt;
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "CadastralBlock", namespace = "http://hubbitus.info/xjc-plugin-demo", propOrder = {
    "number"
})
public class CadastralBlock {
    @XmlElement(required = true)
    protected String number;
}


С плагином же вы получите:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "CadastralBlock", namespace = "http://hubbitus.info/xjc-plugin-demo", propOrder = {
    "number"
})
@XsdInfo(name = "Кадастровый квартал", xsdElementPart = "<complexType name=\"CadastralBlock\">\n  <complexContent>\n    <restriction base=\"{http://www.w3.org/2001/XMLSchema}anyType\">\n      <sequence>\n        <element name=\"number\" type=\"{http://www.w3.org/2001/XMLSchema}string\"/>\n      </sequence>\n    </restriction>\n  </complexContent>\n</complexType>")
public class CadastralBlock {

    @XmlElement(required = true)
    @XsdInfo(name = "Кадастровый номер")
    protected String number;
}


Обратите внимание на добавленные аннотации @XmlType.
Использовать это затем просто, как любую другую аннтотацию:
    XsdInfo xsdAnnotation = CadastralBlock.class.getDeclaredAnnotation(XsdInfo.class);
    System.out.println("XSD description: " + xsdAnnotation.name());


Рабочий пример есть в тестах: 12.


Смотри также популярное:


Зачем нужна Java. http://fetisovvs.blogspot.com/2014/07/java.html
Когда Java наконец помрёт, что с этим делать и что будет с JPoint. https://fetisovvs.blogspot.com/2018/11/java-jpoint-java.html
Разбор основных концепций параллелизма. http://fetisovvs.blogspot.com/2018/04/java.html
Первый контакт с «var» в Java 10. http://fetisovvs.blogspot.com/2018/01/var-java-10-java.html
JAVA 9. Что нового? http://fetisovvs.blogspot.com/2017/10/java-9-java.html
Руководство по Java 9 для тех, кому приходится работать с legacy-кодом. http://fetisovvs.blogspot.com/2018/08/java-9-legacy-java.html
Концепции объектно-ориентированного программирования — ООП в Java. http://fetisovvs.blogspot.com/2017/01/java-java.html
Анимации в Android по полочкам (Часть 1. Базовые анимации). http://fetisovvs.blogspot.com/2018/02/android-1-java.html
Двести пятьдесят русскоязычных обучающих видео докладов и лекций о Java. http://fetisovvs.blogspot.com/2015/12/java-5-java-java.html
Абстрактные классы и методы. http://fetisovvs.blogspot.com/2017/02/java.html
Полное руководство по Java Reflection API. Рефлексия на примерах. http://fetisovvs.blogspot.com/2017/02/java-reflection-api-java.html
Микросервисы для Java программистов. Практическое введение во фреймворки и контейнеры. http://fetisovvs.blogspot.com/2017/10/java-java.html
Микросервисы для Java программистов. Практическое введение во фреймворки и контейнеры. (Часть 3). http://fetisovvs.blogspot.com/2017/10/java-3-java.html
ТОП-3 способа конвертировать массив в ArrayList. Пример на Java. http://fetisovvs.blogspot.com/2016/09/3-arraylist-java-java.html
Ввод–вывод в Java. http://fetisovvs.blogspot.com/2016/05/java-java_28.html
Java Challengers #2: Сравнение строк. https://fetisovvs.blogspot.com/2018/11/java-challengers-2-java.html
Enum-Всемогущий. http://fetisovvs.blogspot.com/2017/02/enum-java.html
Массивы в Java. Создание и обработка. http://fetisovvs.blogspot.com/2017/10/java-java_18.html
Arrays, Collections: Алгоритмический минимум. http://fetisovvs.blogspot.com/2017/12/arrays-collections.html
Популярные методы для работы с Java массивами. http://fetisovvs.blogspot.com/2016/09/java-java_29.html
Пример использования метода replace в Java. Как заменить символ в строке? http://fetisovvs.blogspot.com/2017/01/replace-java-java.html
Класс Scanner в Java — описание и пример использования. http://fetisovvs.blogspot.com/2017/01/scanner-java-java.html
Пример использования метода trim в Java: как удалить пробелы в начале и конце строки? http://fetisovvs.blogspot.com/2017/01/trim-java-java.html
Spark — Потрясающий веб-микрофреймворк для Java. http://fetisovvs.blogspot.com/2017/10/spark-java-java.html
Чтение и запись CSV файла с помощью SuperCSV. http://fetisovvs.blogspot.com/2017/01/csv-supercsv-java-java.html
Конструкция try/catch/finally (исключения). http://fetisovvs.blogspot.com/2017/01/trycatchfinally-java.html
1000+ часов видео по Java на русском. http://fetisovvs.blogspot.nl/2017/06/1000-java-java.html
Раздача халявы: нетормозящие треды в Java. Project Loom. http://fetisovvs.blogspot.com/2018/09/java-project-loom-java.html
Шпаргалка Java программиста 7.1 Типовые задачи: Оптимальный путь преобразования InputStream в строку. http://fetisovvs.blogspot.com/2016/04/java-71-inputstream-java.html
Шпаргалки Java программиста 10: Lombok. http://fetisovvs.blogspot.nl/2017/12/java-10-lombok-java.html
Шпаргалки Java программиста 9: Java SE — Шпаргалка для собеседований и повторений. http://fetisovvs.blogspot.com/2017/12/java-9-java-se-java.html
Шпаргалка Java программиста 8. Библиотеки для работы с Json (Gson, Fastjson,
LoganSquare, Jackson, JsonPath и другие). http://fetisovvs.blogspot.com/2016/04/java-8-json-gson-fastjson-logansquare.html
Java 8 и паттерн Стратегия. http://fetisovvs.blogspot.com/2018/03/java-8-java.html
Java EE Concurency API. http://fetisovvs.blogspot.com/2018/08/java-ee-concurency-api-java.html
Реализация ООП-наследования в классах, работающих с SQL и MS Entity Framework. http://fetisovvs.blogspot.com/2017/02/sql-ms-entity-framework.html
Как установить соединение с СУБД MySQL в IntelliJ IDEA в редакции Community. http://fetisovvs.blogspot.com/2016/04/mysql-intellij-idea-community-java.html
TDD приложений на Spring Boot: работа с базой данных. https://fetisovvs.blogspot.com/2018/12/tdd-spring-boot-java.html
Руководство: Thymeleaf + Spring. 

https://fetisovvs.blogspot.com/2019/01/thymeleaf-spring-1-java.html
https://fetisovvs.blogspot.com/2019/01/thymeleaf-spring-2-java.html
https://fetisovvs.blogspot.com/2019/01/thymeleaf-spring-3-java.html
Максимально простой в поддержке способ интеграции java-клиента с java-сервером. http://fetisovvs.blogspot.com/2018/09/java-java-java.html
Как с помощью maven работать с библиотеками, которых в maven нет. http://fetisovvs.blogspot.com/2017/03/maven-maven-java.html
Проекты по созданию компиляторов из Java в JavaScript и исполняемые файлы. http://fetisovvs.blogspot.com/2018/01/java-javascript-java.html
Реактивное программирование с JAX-RS. http://fetisovvs.blogspot.com/2018/09/jax-rs-java.html
Компактные строки в Java 9. https://fetisovvs.blogspot.com/2018/10/java-9-java.html
Динамический прокси Java: что это и как им пользоваться? https://fetisovvs.blogspot.com/2018/12/java-java.html
Абстрактный CRUD от репозитория до контроллера: что ещё можно сделать при помощи Spring + Generics. http://fetisovvs.blogspot.com/2018/09/crud-spring-generics-java.html
Диагностика утечек памяти в Java. http://fetisovvs.blogspot.com/2017/03/java-java_18.html
Spring AOP и JavaConfig в плагинах для Atlassian Jira. http://fetisovvs.blogspot.com/2018/04/spring-aop-javaconfig-atlassian-jira.html
Блеск и нищета Java для настольных систем. http://fetisovvs.blogspot.com/2018/04/java-haulmont-java.html
Разбор задачек от Одноклассников на JPoint 2018. http://fetisovvs.blogspot.com/2018/04/jpoint-2018-java.html
Программируем… выход из лабиринта. http://fetisovvs.blogspot.com/2015/10/java.html
Основы работы с IntelliJ IDEA. Интерфейс программы. http://fetisovvs.blogspot.com/2016/09/intellij-idea-java.html
Ускоряем время сборки и доставки java web приложения. http://fetisovvs.blogspot.com/2018/03/java-web-java.html
Открытый урок Java Enterprise «CDI in action». http://fetisovvs.blogspot.com/2018/09/java-enterprise-cdi-in-action-java.html
«Мы все стремимся к сложности, а потом с ней боремся»: интервью с Венкатом Субраманиамом. http://fetisovvs.blogspot.com/2018/09/java_16.html

Комментариев нет:

Отправить комментарий