«1. Введение

Это вводная статья о JAXB (Java Architecture for XML Binding).

Сначала мы покажем, как преобразовывать объекты Java в XML и наоборот, а затем сосредоточимся на создании классов Java из схемы XML и наоборот с помощью подключаемого модуля JAXB-2 Maven.

2. Обзор

JAXB предоставляет быстрый и удобный способ маршалирования (записи) объектов Java в XML и демаршалирования (чтения) XML в объекты. Он поддерживает структуру привязки, которая сопоставляет элементы и атрибуты XML с полями и свойствами Java с помощью аннотаций Java.

Плагин JAXB-2 Maven делегирует большую часть своей работы одному из двух инструментов, поставляемых JDK: XJC и Schemagen.

3. Аннотации JAXB

JAXB использует аннотации Java для дополнения сгенерированных классов дополнительной информацией. Добавление таких аннотаций к существующим классам Java подготавливает их к среде выполнения JAXB.

Давайте сначала создадим простой объект Java, чтобы проиллюстрировать маршалинг и демаршаллинг:

@XmlRootElement(name = "book")
@XmlType(propOrder = { "id", "name", "date" })
public class Book {
    private Long id;
    private String name;
    private String author;
    private Date date;

    @XmlAttribute
    public void setId(Long id) {
        this.id = id;
    }

    @XmlElement(name = "title")
    public void setName(String name) {
        this.name = name;
    }

    @XmlTransient
    public void setAuthor(String author) {
        this.author = author;
    }
    
    // constructor, getters and setters
}

Приведенный выше класс содержит следующие аннотации:

    @XmlRootElement: имя корневого элемента XML получено из имя класса, и мы также можем указать имя корневого элемента XML, используя его атрибут имени @XmlType: определить порядок, в котором поля записываются в файле XML @XmlElement: определить фактическое имя элемента XML, которое будет использоваться @ XmlAttribute: определите, что поле id отображается как атрибут вместо элемента @XmlTransient: аннотируйте поля, которые мы не хотим включать в XML

Для получения более подробной информации об аннотации JAXB вы можете проверить следующую ссылку .

4. Маршаллинг — преобразование объекта Java в XML

Маршаллинг предоставляет клиентскому приложению возможность конвертировать дерево объектов Java, полученное из JAXB, в данные XML. По умолчанию Marshaller использует кодировку UTF-8 при создании XML-данных. Далее мы создадим файлы XML из объектов Java.

Давайте создадим простую программу, используя JAXBContext, которая предоставляет абстракцию для управления информацией о привязке XML/Java, необходимой для реализации операций структуры привязки JAXB:

public void marshal() throws JAXBException, IOException {
    Book book = new Book();
    book.setId(1L);
    book.setName("Book1");
    book.setAuthor("Author1");
    book.setDate(new Date());

    JAXBContext context = JAXBContext.newInstance(Book.class);
    Marshaller mar= context.createMarshaller();
    mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    mar.marshal(book, new File("./book.xml"));
}

Класс javax.xml.bind.JAXBContext предоставляет клиентскую точка входа в JAXB API. По умолчанию JAXB не форматирует XML-документ. Это экономит место и предотвращает случайную интерпретацию любого пробела как значимого.

Чтобы форматировать вывод JAXB, мы просто устанавливаем для свойства Marshaller.JAXB_FORMATTED_OUTPUT значение true на Marshaller. Метод маршала использует объект и выходной файл, в котором в качестве параметров сохраняется сгенерированный XML.

Когда мы запускаем приведенный выше код, мы можем проверить результат в book.xml, чтобы убедиться, что мы успешно преобразовали объект Java в данные XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book id="1">
    <title>Book1</title>
    <date>2016-11-12T11:25:12.227+07:00</date>
</book>

5. Де-сортировка — преобразование XML в Java Object

Un-marshalling предоставляет клиентскому приложению возможность преобразовывать XML-данные в производные JAXB-объекты Java.

Давайте воспользуемся JAXB Unmarshaller для демаршалирования нашего book.xml обратно в объект Java:

public Book unmarshall() throws JAXBException, IOException {
    JAXBContext context = JAXBContext.newInstance(Book.class);
    return (Book) context.createUnmarshaller()
      .unmarshal(new FileReader("./book.xml"));
}

Когда мы запустим приведенный выше код, мы можем проверить вывод консоли, чтобы убедиться, что мы успешно преобразовали данные XML в объект Java:

Book [id=1, name=Book1, author=null, date=Sat Nov 12 11:38:18 ICT 2016]

6. Сложные типы данных

При обработке сложных типов данных, которые могут быть недоступны напрямую в JAXB, мы можем написать адаптер, чтобы указать JAXB, как управлять конкретным типом.

Используя JAXB XmlAdapter, мы можем определить собственный код для преобразования неотображаемого класса в то, что может обрабатывать JAXB. Аннотация @XmlJavaTypeAdapter использует адаптер, который расширяет класс XmlAdapter для настраиваемого маршалинга.

Давайте создадим адаптер для указания формата даты при маршалинге:

public class DateAdapter extends XmlAdapter<String, Date> {

    private static final ThreadLocal<DateFormat> dateFormat 
      = new ThreadLocal<DateFormat>() {

        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };

    @Override
    public Date unmarshal(String v) throws Exception {
        return dateFormat.get().parse(v);
    }

    @Override
    public String marshal(Date v) throws Exception {
        return dateFormat.get().format(v);
    }
}

Мы используем формат даты «гггг-ММ-дд ЧЧ:мм:сс» для преобразования даты в строку при маршалинге и ThreadLocal для создания наш DateFormat потокобезопасен.

Давайте применим DateAdapter к нашей книге:

@XmlRootElement(name = "book")
@XmlType(propOrder = { "id", "name", "date" })
public class Book {
    private Long id;
    private String name;
    private String author;
    private Date date;

    @XmlAttribute
    public void setId(Long id) {
        this.id = id;
    }

    @XmlTransient
    public void setAuthor(String author) {
        this.author = author;
    }

    @XmlElement(name = "title")
    public void setName(String name) {
        this.name = name;
    }

    @XmlJavaTypeAdapter(DateAdapter.class)
    public void setDate(Date date) {
        this.date = date;
    }
}

Когда мы запустим приведенный выше код, мы можем проверить результат в book.xml, чтобы убедиться, что мы успешно преобразовали наш объект Java в XML, используя новую дату формат «гггг-ММ-дд ЧЧ: мм: сс»:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book id="1">
    <title>Book1</title>
    <date>2016-11-10 23:44:18</date>final
</book>

7. Плагин JAXB-2 Maven

«Этот подключаемый модуль использует API Java для привязки XML (JAXB), версия 2+, для создания классов Java из схем XML (и, возможно, файлов привязки) или для создания схемы XML из аннотированного класса Java.

Обратите внимание, что существует два основных подхода к созданию веб-сервисов: Contract Last и Contract First. Для получения более подробной информации об этих подходах вы можете проверить следующую ссылку.

7.1. Генерация класса Java из XSD

Плагин JAXB-2 Maven использует предоставленный JDK инструмент XJC, инструмент компилятора привязки JAXB, который генерирует классы Java из XSD (определение схемы XML).

Давайте создадим простой файл user.xsd и воспользуемся плагином JAXB-2 Maven для создания классов Java из этой схемы XSD:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
    targetNamespace="/jaxb/gen"
    xmlns:userns="/jaxb/gen"
    elementFormDefault="qualified">

    <element name="userRequest" type="userns:UserRequest"></element>
    <element name="userResponse" type="userns:UserResponse"></element>

    <complexType name="UserRequest">
        <sequence>
            <element name="id" type="int" />
            <element name="name" type="string" />
        </sequence>
    </complexType>

    <complexType name="UserResponse">
        <sequence>
            <element name="id" type="int" />
            <element name="name" type="string" />
            <element name="gender" type="string" />
            <element name="created" type="dateTime" />
        </sequence>
    </complexType>
</schema>

Давайте настроим плагин JAXB-2 Maven:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxb2-maven-plugin</artifactId>
    <version>2.3</version>
    <executions>
        <execution>
            <id>xjc</id>
            <goals>
                <goal>xjc</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <xjbSources>
            <xjbSource>src/main/resources/global.xjb</xjbSource>
        </xjbSources>
        <sources>
            <source>src/main/resources/user.xsd</source>
        </sources>
        <outputDirectory>${basedir}/src/main/java</outputDirectory>
        <clearOutputDir>false</clearOutputDir>
    </configuration>
</plugin>

Автор по умолчанию этот плагин находит файлы XSD в src/main/xsd. Мы можем настроить поиск XSD, соответствующим образом изменив раздел конфигурации этого плагина в pom.xml.

По умолчанию эти классы Java генерируются в папке target/generated-resources/jaxb. Мы можем изменить выходной каталог, добавив элемент outputDirectory в конфигурацию плагина. Мы также можем добавить элемент clearOutputDir со значением false, чтобы предотвратить стирание файлов в этом каталоге.

Мы также можем настроить глобальную привязку JAXB, которая переопределяет правила привязки по умолчанию:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxb:bindings version="2.0" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    jaxb:extensionBindingPrefixes="xjc">

    <jaxb:globalBindings>
        <xjc:simple />
        <xjc:serializable uid="-1" />
        <jaxb:javaType name="java.util.Calendar" xmlType="xs:dateTime"
            parse="javax.xml.bind.DatatypeConverter.parseDateTime"
            print="javax.xml.bind.DatatypeConverter.printDateTime" />
    </jaxb:globalBindings>
</jaxb:bindings>

Приведенный выше файл global.xjb переопределяет тип dateTime на тип java.util.Calendar. Когда мы собираем проект, он создает файлы классов в папке src/main/java и пакет com.baeldung.jaxb.gen.

7.2. Генерация XSD-схемы из Java

В том же подключаемом модуле используется инструмент Schemagen, поставляемый с JDK. Это инструмент компилятора JAXB Binding, который может генерировать схему XSD из классов Java. Чтобы класс Java соответствовал требованиям кандидата схемы XSD, класс должен быть снабжен аннотацией @XmlType.

Мы повторно используем файлы классов Java из предыдущего примера. Давайте настроим плагин:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxb2-maven-plugin</artifactId>
    <version>2.3</version>
    <executions>
        <execution>
            <id>schemagen</id>
            <goals>
                <goal>schemagen</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <sources>
            <source>src/main/java/com/baeldung/jaxb/gen</source>
        </sources>
        <outputDirectory>src/main/resources</outputDirectory>
        <clearOutputDir>false</clearOutputDir>
        <transformSchemas>
            <transformSchema>
                <uri>/jaxb/gen</uri>
                <toPrefix>user</toPrefix>
                <toFile>user-gen.xsd</toFile>
            </transformSchema>
        </transformSchemas>
    </configuration>
</plugin>

По умолчанию JAXB рекурсивно сканирует все папки в src/main/java на наличие аннотированных классов JAXB. Мы можем указать другую исходную папку для наших аннотированных классов JAXB, добавив исходный элемент в конфигурацию плагина.

Мы также можем зарегистрировать transformSchemas, постпроцессор, ответственный за присвоение имени XSD-схеме. Он работает путем сопоставления пространства имен с пространством имен @XmlType вашего класса Java.

Когда мы собираем проект, он создает файл user-gen.xsd в каталоге src/main/resources.

8. Заключение

В этой статье мы рассмотрели вводные концепции JAXB. Для получения подробной информации мы можем взглянуть на домашнюю страницу JAXB.

Исходный код этой статьи можно найти на GitHub.