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чч:мм:сс»
, где « 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 .