Недавно передо мной встала задача сгенерировать Java классы на основе документации, представленной в виде xsd схем. С этим был призван справиться JAXB, а именно его инструмент xjc. Запустить его можно через командную строку, задачу Ant, или через разнообразные maven или gradle плагины.
Компилятор xjc предоставляет возможности настроить некоторые параметры генерации с помощью разных опций: нужно обязательно указать путь до xml схем, а также можно для java классов указать source директорию, package и некоторые другие параметры, о которых можно подробнее прочитать в документации. Этот базовый минимум кроме непосредственно самого компилятора как правило предоставляет любой плагин, использующий xjc.
Но для моей задачи этого оказалось мало – требовалась более тонкая настройка генерации. Для этого в xjc можно использовать свои биндинги, определяющие, как будут xml-структуры связываться классами java, как они будут обрабатываться. Например, биндинг будет полезен, когда нам нужно задать пользовательские имена полей, изменить типы данных для полей, чтобы они соответствовали ожиданиям проекта, создать документацию к сгенерированным классам или управлять, какие элементы и атрибуты будут генерироваться, а какие — игнорироваться.
Настройки биндингов необходимо выполнять вручную. Биндинги могут быть встроены в виде аннотаций в исходные xml схемы, или передаваться отдельным, внешним файлом. Первый вариант не подходил мне, так как я не хотела менять исходные схемы в документации. Кроме того, если бы документация поменялась, не хотелось бы настраивать биндинги заново.
Сразу разберем пример внешнего биндинга. Для моей задачи необходимо было, чтобы корневые элементы были переименованы некоторым образом. Все сущности в качестве обертки использовали элемент, имеющий одинаковое название – а нам необходимо было их различать. Я использовала биндинг, который переименовывает ноды <node> в схеме <schemalocation> на <newName>:
<jxb:bindings version="3.0"
xmlns:jxb="https://jakarta.ee/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jxb:globalBindings>
<jxb:serializable uid="1"/>
</jxb:globalBindings>
<jxb:bindings schemaLocation="<schemalocation>" node="/xs:schema">
<jxb:bindings node="//xs:element[@name='<node>']">
<jxb:class name="<newName>"/>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
Для формирования внешних биндингов есть несколько правил:
- Файл настройки привязки должен начинаться с атрибута jxb:bindings version, а также содержать атрибуты для пространств имён JAXB и XMLSchema:
<jxb:bindings version="3.0"
xmlns:jxb="https://jakarta.ee/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
Здесь мы используем пространсство имен https://jakarta.ee/xml/ns/jaxb, чтобы аннотации были из актуального пакета jakarta, а не javax
- Объявление jxb:globalBindings определяет глобальные настройки для JAXB-биндингов:
<jxb:globalBindings>
<jxb:serializable uid="1"/>
</jxb:globalBindings>
Здесь указываем, что создаваемые классы будут реализовывать интерфейс Serializable. Атрибут uid="1" задает уникальный идентификатор для сериализации.
- Первое объявление schemaLocation/node указывает имя схемы и корневой узел схемы. Объединяет биндинги, применяемые к одной схеме:
<jxb:bindings schemaLocation="<schemalocation>" node="/xs:schema">
- Далее можем указывать узлы, к которым хотим применить биндинг:
<jxb:bindings node="//xs:element[@name='<node>']">
- Указываем сами настройки биндингов:
<jxb:class name="<newName>"/>
Тег <class> позволяет задать настройки того, как элементы схемы будут преобразованы в java классы. С помощью него можно задать имя сгенерированного класса, как в моем примере, или, например, класс, реализующий элемент схемы.
Кроме тега <class> есть
- <property> для настройки сопоставление элементов или атрибутов схемы с полями классов java
- <javaType> для настройки сопоставление типов данных xml с типами данных java
- <typesafeEnumClass> для создания enum’ов для определенных элементов или атрибутов xml схемы
- <javadoc> для добавления пользовательских аннотаций javadoc к пакетам, классам, интерфейсам, методам и полям сгенерированных классов
Подробнее про написание биндингов и синтаксис этих настроек можно почитать здесь.
Таким образом, пользовательские биндинги добавляют неповоротливому xjc возможности чуть более тонкой настройки генерации.