Перейти к основному содержимому

Unmarshalling Dates Используя JAXB

· 6 мин. чтения

1. Введение

В этом уроке мы увидим, как распараллеливать объекты даты с разными форматами с помощью JAXB .

Сначала мы рассмотрим формат даты схемы по умолчанию. Затем мы рассмотрим, как использовать различные форматы. Мы также увидим, как мы можем справиться с общей проблемой, возникающей с помощью этих методов.

2. Привязка схемы к Java

Во- первых, нам нужно понять взаимосвязь между XML-схемой и типами данных Java . В частности, нас интересует сопоставление между XML-схемой и объектами даты Java.

Согласно отображению Schema to Java , нам необходимо учитывать три типа данных Schema: xsd:date , xsd:time и xsd:dateTime . Как мы видим, все они сопоставлены с javax.xml.datatype.XMLGregorianCalendar .

Нам также необходимо понять форматы по умолчанию для этих типов схемы XML. Типы данных xsd:date и xsd:time имеют форматы « ГГГГ-ММ-ДД» и « чч:мм:сс» . Формат xsd:dateTime — « ГГГГ-ММ-ДДTчч:мм:сс» , где « — разделитель, указывающий начало временного интервала.

3. Использование формата даты схемы по умолчанию

Мы собираемся создать пример, который неупорядочивает объекты даты. Давайте сосредоточимся на типе данных xsd:dateTime , так как он представляет собой надмножество других типов.

Давайте воспользуемся простым XML-файлом, описывающим книгу:

<book>
<title>Book1</title>
<published>1979-10-21T03:31:12</published>
</book>

Мы хотим сопоставить файл с соответствующим объектом Java Book :

@XmlRootElement(name = "book")
public class Book {

@XmlElement(name = "title", required = true)
private String title;

@XmlElement(name = "published", required = true)
private XMLGregorianCalendar published;

@Override
public String toString() {
return "[title: " + title + "; published: " + published.toString() + "]";
}

}

Наконец, нам нужно создать клиентское приложение, которое преобразует данные XML в объекты Java, производные от JAXB:

public static Book unmarshalDates(InputStream inputFile) 
throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(Book.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
return (Book) jaxbUnmarshaller.unmarshal(inputFile);
}

В приведенном выше коде мы определили JAXBContext , который является точкой входа в JAXB API. Затем мы использовали JAXB Unmarshaller для входного потока, чтобы прочитать наш объект:

Если мы запустим приведенный выше код и распечатаем результат, мы получим следующий объект Book :

[title: Book1; published: 1979-11-28T02:31:32]

Следует отметить, что, несмотря на то, что сопоставлением по умолчанию для xsd:dateTime является XMLGregorianCalendar , мы также могли бы использовать более распространенные типы Java: java.util.Date и java.util.Calendar , согласно руководству пользователя JAXB .

4. Использование пользовательского формата даты

Приведенный выше пример работает, потому что мы используем формат даты схемы по умолчанию «ГГГГ-ММ-ДДTчч:мм:сс».

Но что, если мы хотим использовать другой формат, например «ГГГГ-ММ-ДД чч:мм:сс», избавившись от разделителя «Т» ? Если бы мы заменили разделитель символом пробела в нашем XML-файле, неупорядочивание по умолчанию завершилось бы неудачей.

4.1. Создание пользовательского XmlAdapter

Чтобы использовать другой формат даты, нам нужно определить XmlAdapter .

Давайте также посмотрим, как сопоставить тип xsd:dateTime с объектом java.util.Date с помощью нашего пользовательского XmlAdapter:

public class DateAdapter extends XmlAdapter<String, Date> {

private static final String CUSTOM_FORMAT_STRING = "yyyy-MM-dd HH:mm:ss";

@Override
public String marshal(Date v) {
return new SimpleDateFormat(CUSTOM_FORMAT_STRING).format(v);
}

@Override
public Date unmarshal(String v) throws ParseException {
return new SimpleDateFormat(CUSTOM_FORMAT_STRING).parse(v);
}

}

В этом адаптере мы использовали SimpleDateFormat для форматирования даты. Мы должны быть осторожны, так как SimpleDateFormat не является потокобезопасным . Чтобы избежать проблем с общим объектом SimpleDateFormat в нескольких потоках , мы создаем новый каждый раз, когда он нам нужен. `` `` ``

4.2. Внутреннее устройство XmlAdapter

Как мы видим, XmlAdapter имеет два параметра типа , в данном случае String и Date . Первый тип используется внутри XML и называется типом значения. В этом случае JAXB знает, как преобразовать значение XML в String . Второй тип называется связанным типом и относится к значению в нашем объекте Java.

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

Чтобы создать собственный Xml - адаптер , нам нужно переопределить два метода: XmlAdapter.marshal() и XmlAdapter.unmarshal() .

Во время демаршалинга структура привязки JAXB сначала демаршалирует XML-представление в String , а затем вызывает DateAdapter.unmarshal() , чтобы адаптировать тип значения к Date . Во время сортировки структура привязки JAXB вызывает DateAdapter.marshal() для адаптации Date к String , которая затем маршалируется в XML-представление.

4.3. Интеграция через аннотации JAXB

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

@XmlRootElement(name = "book")
public class BookDateAdapter {

// same as before

@XmlElement(name = "published", required = true)
@XmlJavaTypeAdapter(DateAdapter.class)
private Date published;

// same as before

}

Мы также используем стандартные аннотации JAXB : аннотации @XmlRootElement и @XmlElement .

Наконец, давайте запустим новый код:

[title: Book1; published: Wed Nov 28 02:31:32 EET 1979]

5. Unmarshalling Dates в Java 8

В Java 8 появился новый API даты/времени . Здесь мы сосредоточимся на классе LocalDateTime , который является одним из наиболее часто используемых.

5.1. Создание XmlAdapter на основе LocalDateTime

По умолчанию JAXB не может автоматически привязать значение xsd:dateTime к объекту LocalDateTime независимо от формата даты. Чтобы преобразовать значение даты XML-схемы в объект LocalDateTime или из него , нам нужно определить еще один XmlAdapter, аналогичный предыдущему:

public class LocalDateTimeAdapter extends XmlAdapter<String, LocalDateTime> {

private DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

@Override
public String marshal(LocalDateTime dateTime) {
return dateTime.format(dateFormat);
}

@Override
public LocalDateTime unmarshal(String dateTime) {
return LocalDateTime.parse(dateTime, dateFormat);
}

}

В этом случае мы использовали DateTimeFormatter вместо SimpleDateFormat . Первый был представлен в Java 8 и совместим с новым API даты/времени .

Обратите внимание, что операции преобразования могут совместно использовать объект DateTimeFormatter , поскольку DateTimeFormatter является потокобезопасным. ``

5.2. Интеграция нового адаптера

Теперь давайте заменим старый адаптер новым в нашем классе Book , а также Date на LocalDateTime :

@XmlRootElement(name = "book")
public class BookLocalDateTimeAdapter {

// same as before

@XmlElement(name = "published", required = true)
@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
private LocalDateTime published;

// same as before

}

Если мы запустим приведенный выше код, мы получим вывод:

[title: Book1; published: 1979-11-28T02:31:32]

Обратите внимание, что LocalDateTime.toString() добавляет разделитель «T» между датой и временем.

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

В этом руководстве мы рассмотрели неупорядочивание дат с помощью JAXB .

Во-первых, мы рассмотрели сопоставление XML-схемы с типом данных Java и создали пример, используя формат даты XML-схемы по умолчанию.

Затем мы узнали, как использовать настраиваемый формат даты на основе пользовательского XmlAdapter, и увидели, как обеспечить безопасность потоков SimpleDateFormat .

Наконец, мы использовали превосходный поточно-ориентированный API даты/времени Java 8 и неупорядоченные даты с пользовательскими форматами.

Как всегда, исходный код, использованный в руководстве, доступен на GitHub .