Java JAXB
Отображение XML на объекты Java и обратно с помощью аннотаций JAXB и Marshaller/Unmarshaller.
JAXB (Jakarta XML Binding, ранее Java Architecture for XML Binding) отображает объекты Java на XML и обратно без необходимости писать код разбора вручную. Вы аннотируете обычный класс, после чего передаёте его в Marshaller для получения XML или в Unmarshaller для чтения XML в объекты. JAXB входил в состав JDK (javax.xml.bind) вплоть до Java 8, был удалён в Java 11 и теперь поставляется как отдельная зависимость в пространстве имён jakarta.xml.bind. Аннотации и модель маршалинга/анмаршалинга одинаковы в обоих вариантах.
Эта глава охватывает что такое привязка JAXB, основные аннотации, как маршалировать объект в XML и анмаршалировать XML обратно, как отображаются коллекции, а также смену пространства имён между Java 8 и современной Java. JAXB — это binding API: в отличие от низкоуровневых парсеров DOM и SAX, рассмотренных ранее в этой части, вы никогда не работаете с деревом XML напрямую — вы работаете с обычными объектами Java.
Когда стоит использовать JAXB
Используйте JAXB, когда ваши данные уже имеют (или заслуживают) класс, а XML — лишь формат передачи или хранения:
- Чтение и запись конфигурационных или документных файлов, где структура стабильна и известна заранее.
- SOAP / устаревшие веб-сервисы, где контракт — это XML-схема, а классы генерируются автоматически.
- Двустороннее преобразование — загрузка XML, изменение объекта и запись обратно без ручного разбора.
Используйте DOM или SAX, когда структура нерегулярна, вам нужны лишь несколько полей из большого документа или нет естественного класса для привязки. А если вы контролируете оба конца и вам нужен компактный формат данных, JSON с Jackson обычно легче, чем XML.
Главная идея: аннотации описывают отображение
Вы не пишете код, обходящий дерево XML. Вместо этого вы описываете с помощью аннотаций на классе, как его поля соответствуют элементам и атрибутам XML. JAXB читает эти аннотации во время выполнения и генерирует преобразование в обоих направлениях. POJO становится самодокументирующейся схемой.
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlAttribute;
@XmlRootElement(name = "book")
public class Book {
private String title;
private String author;
private int year;
@XmlElement public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
@XmlElement public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
@XmlAttribute public int getYear() { return year; }
public void setYear(int year) { this.year = year; }
// JAXB requires a public no-arg constructor for unmarshalling
public Book() {}
}Основные аннотации
Небольшой набор аннотаций охватывает почти любое отображение. Они находятся в пакете jakarta.xml.bind.annotation (или javax.xml.bind.annotation в Java 8).
| Аннотация | Назначение |
|---|---|
@XmlRootElement | Отмечает класс как корень документа; задаёт имя внешнего элемента |
@XmlElement | Отображает поле/свойство на вложенный элемент |
@XmlAttribute | Отображает поле/свойство на атрибут его элемента |
@XmlElementWrapper | Оборачивает коллекцию в содержащий элемент |
@XmlTransient | Полностью исключает поле из XML |
@XmlAccessorType | Управляет тем, что JAXB привязывает по умолчанию — поля или геттеры |
Маршалинг: объект в XML
JAXBContext — это точка входа: создайте его для ваших корневых классов, затем запросите Marshaller. Маршаллер превращает граф объектов в XML. Установка JAXB_FORMATTED_OUTPUT даёт удобочитаемый вывод с отступами.
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Marshaller;
Book book = new Book();
book.setTitle("Effective Java");
book.setAuthor("Joshua Bloch");
book.setYear(2018);
JAXBContext context = JAXBContext.newInstance(Book.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(book, System.out);
// <book year="2018"><title>Effective Java</title><author>Joshua Bloch</author></book>Анмаршалинг: XML в объект
Обратная операция симметрична: запросите у того же JAXBContext Unmarshaller и укажите ему источник — File, InputStream, Reader или StringReader. JAXB создаёт объект с помощью конструктора без аргументов и заполняет его из элементов и атрибутов.
import jakarta.xml.bind.Unmarshaller;
import java.io.StringReader;
String xml = "<book year=\"2018\">"
+ "<title>Effective Java</title>"
+ "<author>Joshua Bloch</author></book>";
Unmarshaller unmarshaller = context.createUnmarshaller();
Book book = (Book) unmarshaller.unmarshal(new StringReader(xml));
System.out.println(book.getTitle()); // Effective Java
System.out.println(book.getYear()); // 2018JAXB недоступен в этой среде выполнения (это внешняя зависимость в современной Java), поэтому приведённый ниже рабочий пример демонстрирует тот же цикл маршалинга/анмаршалинга с использованием только встроенного в JDK DOM API. Концепция идентична: атрибут на корневом элементе, дочерние элементы для полей и обратный цикл к равному объекту.
Что следует вынести из запуска:
- Маршалированный XML помещает
yearкак атрибут в<book>, аtitleиauthor— как дочерние элементы — именно такое разделение управляется@XmlAttributeи@XmlElementв реальном JAXB. Выбор аннотации определяет, что будет элементом, а что — атрибутом. - Корневой тег —
book, о чём сообщаетel.getTagName(). В JAXB это имя задаётся через@XmlRootElement(name = "book"); здесь — строка, переданная вcreateElement. В любом случае внешний элемент идентифицирует тип документа. - Маршалинг и анмаршалинг — зеркальные операции над одной и той же структурой: программа строит XML из
Book, затем восстанавливаетBookиз этого XML.MarshallerиUnmarshallerJAXB — это именно такая пара, опирающаяся на одинJAXBContext. round-trip equal : trueдоказывает, что данные пережили преобразование без потерь — title, author и year вернулись без изменений. Корректная привязка не теряет данные — это именно то свойство, на которое вы рассчитываете, когда XML является форматом передачи данных.- Для чтения
yearобратно потребовалсяInteger.parseInt, потому что XML — это всё текст. JAXB скрывает это, автоматически преобразуя текст атрибутов и элементов в объявленный тип Java (int,LocalDate,BigDecimal); без него каждое поле — это строка, которую нужно разбирать вручную.
Отображение коллекций
List отображается на повторяющиеся элементы. По умолчанию каждый элемент называется по имени поля, что может дать плоский, трудночитаемый документ. @XmlElementWrapper добавляет содержащий элемент, чтобы элементы были сгруппированы — это распространённый, читаемый паттерн.
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlElementWrapper;
import java.util.List;
@XmlRootElement(name = "library")
public class Library {
private List<Book> books;
@XmlElementWrapper(name = "books") // outer <books> element
@XmlElement(name = "book") // each item is a <book>
public List<Book> getBooks() { return books; }
public void setBooks(List<Book> books) { this.books = books; }
public Library() {}
}С оберткой вывод получается аккуратно вложенным:
<library>
<books>
<book year="2018"><title>Effective Java</title>...</book>
<book year="2008"><title>Clean Code</title>...</book>
</books>
</library>Уберите @XmlElementWrapper, и элементы <book> окажутся непосредственно под <library> без группирующего элемента — допустимо, но более плоско. Выбор между этими двумя вариантами — наиболее распространённое решение при отображении коллекций в JAXB.
Java 8 vs. современная Java: смена пространства имён
Главная ловушка — переименование пакета. В Java 8 API входит в поставку и находится в javax.xml.bind. Начиная с Java 11 он вынесен и находится в jakarta.xml.bind, подключаемом как зависимость.
| Java 8 | Java 11+ | |
|---|---|---|
| Пакет | javax.xml.bind | jakarta.xml.bind |
| В classpath? | Встроен | Добавьте зависимость |
| Артефакт времени выполнения | JDK | org.glassfish.jaxb:jaxb-runtime |
Для сборки Maven на современной Java добавьте API и реализацию времени выполнения:
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.5</version>
</dependency>Практика
Смотрите также
- Работа с XML в Java — обзор XML API и применимость каждого из них.
- DOM-парсер и SAX-парсер — низкоуровневые альтернативы привязке.
- Jackson — та же идея аннотирования и привязки, применённая к JSON.