Unmarshalling Dates Using JAXB – 使用JAXB解读日期

最后修改: 2019年 9月 17日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this tutorial, we’re going to see how to unmarshal date objects with different formats using JAXB.

在本教程中,我们将看到如何使用JAXB来解读不同格式的日期对象。

First, we’ll cover the default schema date format. Then, we’ll explore how to use different formats. We’ll also see how we can handle a common challenge that arises with these techniques.

首先,我们将介绍默认的模式日期格式。然后,我们将探讨如何使用不同的格式。我们还将看到我们如何处理这些技术中出现的一个常见挑战。

2. Schema to Java Binding

2.模式到Java的绑定

First, we need to understand the relationship between the XML Schema and Java data types. In particular, we’re interested in the mapping between an XML Schema and Java date objects.

首先,我们需要了解XML模式和Java数据类型之间的关系。特别是,我们对XML Schema和Java日期对象之间的映射感兴趣。

According to the Schema to Java mapping, there are three Schema data types that we need to take into account: xsd:date, xsd:time and xsd:dateTime. As we can see, all of them are mapped to javax.xml.datatype.XMLGregorianCalendar.

根据Schema到Java的映射,有三种Schema数据类型是我们需要考虑到的。xsd:date, xsd:timexsd:dateTime。我们可以看到,它们都被映射到了javax.xml.datatype.XMLGregorianCalendar

We also need to understand the default formats for these XML Schema types. The xsd:date and xsd:time data types have “YYYY-MM-DD” and “hh:mm:ss” formats. The xsd:dateTime format is “YYYY-MM-DDThh:mm:ss” where “T” is a separator indicating the start of the time section.

我们还需要了解这些XML Schema类型的默认格式xsd:datexsd:time数据类型有”YYYY-MM-DD”和”hh:mm:ss”格式。xsd:dateTime格式是”YYY-MM-DDThh:mm:ss” 其中”T”是一个分隔符,表示时间部分的开始。

3. Using the Default Schema Date Format

3.使用默认模式的日期格式

We’re going to build an example that unmarshals date objects. Let’s focus on the xsd:dateTime data type because it’s a superset of the other types.

我们将建立一个例子来解读日期对象。让我们专注于 xsd:dateTime 数据类型,因为它是其他类型的超集。

Let’s use a simple XML file that describes a book:

让我们用一个简单的XML文件来描述一本书。

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

We want to map the file to the corresponding Java Book object:

我们想把文件映射到相应的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() + "]";
    }

}

Finally, we need to create a client application that converts the XML data to JAXB-derived Java objects:

最后,我们需要创建一个客户端应用程序,将XML数据转换为JAXB派生的Java对象。

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

In the above code, we’ve defined a JAXBContext which is the entry-point into the JAXB API. Then, we’ve used a JAXB Unmarshaller on an input stream in order to read our object:

在上面的代码中,我们定义了一个JAXBContext,这是进入JAXB API的入口。然后,我们在输入流上使用了一个JAXB Unmarshaller,以读取我们的对象。

If we run the above code and print the result, we’ll get the following Book object:

如果我们运行上述代码并打印结果,我们将得到以下Book对象。

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

We should note that, even though the default mapping for xsd:dateTime is the XMLGregorianCalendar, we could also have used the more common Java types: java.util.Date and java.util.Calendar, according to the JAXB user guide.

我们应该注意,尽管 xsd:dateTime 的默认映射是 XMLGregorianCalendar,我们也可以使用更常见的 Java 类型。java.util.Datejava.util.Calendar,根据JAXB 用户指南

4. Using a Custom Date Format

4.使用自定义日期格式

The above example works because we’re using the default schema date format, “YYYY-MM-DDThh:mm:ss”.

上面的例子之所以有效,是因为我们使用了默认的模式日期格式,“YYYY-MM-DDThh:mm:ss”。

But what if we want to use another format like “YYYY-MM-DD hh:mm:ss”, getting rid of the “T” delimiter? If we were to replace the delimiter with a space character in our XML file, the default unmarshalling would fail.

但是,如果我们想使用另一种格式,如“YYYY-MM-DD hh:mm:ss”,摆脱“T”分隔符呢?如果我们要在我们的XML文件中用空格字符替换分隔符,那么默认的解读将失败。

4.1. Building a Custom XmlAdapter

4.1.建立一个自定义的XmlAdapter

In order to use a different date format, we need to define an XmlAdapter.

为了使用不同的日期格式,我们需要定义一个XmlAdapter

Let’s also see how to map the xsd:dateTime type to a java.util.Date object with our custom XmlAdapter:

让我们看看如何用我们的自定义XmlAdapter将xsd:dateTime类型映射到java.util.Date对象。

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);
    }

}

In this adapter, we’ve used SimpleDateFormat to format our date. We need to be careful as the SimpleDateFormat is not thread-safe. To avoid multiple threads experiencing issues with a shared SimpleDateFormat object, we are creating a new one each time we need it.

在这个适配器中,我们使用SimpleDateFormat来格式化我们的日期。我们需要小心,因为SimpleDateFormat不是thread-safe为了避免多个线程在共享的SimpleDateFormat对象上遇到问题,我们在每次需要它时都会创建一个新的对象。

4.2. The XmlAdapter‘s Internals

4.2.XmlAdapter的内部结构

As we can see, the XmlAdapter has two type parameters, in this case, String and Date. The first one is the type used inside the XML and is called the value type. In this case, JAXB knows how to convert an XML value into a String. The second one is called the bound type and relates to the value in our Java object.

我们可以看到,XmlAdapter有两个类型参数,在本例中,StringDate。第一个是在XML中使用的类型,被称为值类型。在这种情况下,JAXB 知道如何将一个 XML 值转换成 String。第二个被称为绑定类型,与我们的Java对象中的值有关。

The objective of an adapter is to convert between the value type and a bound type, in a way that JAXB cannot do by default.

适配器的目标是在值类型和绑定类型之间进行转换,这是JAXB默认不能做到的。

In order to build a custom XmlAdapter, we have to override two methods: XmlAdapter.marshal() and XmlAdapter.unmarshal().

为了建立一个自定义的XmlAdapter,我们必须覆盖两个方法。XmlAdapter.marshal()XmlAdapter.unmarshal()

During unmarshalling, the JAXB binding framework first unmarshals the XML representation to a String and then invokes DateAdapter.unmarshal() to adapt the value type to a Date. During marshalling, the JAXB binding framework invokes DateAdapter.marshal() to adapt a Date to String, which is then marshaled to an XML representation.

在解封期间,JAXB 绑定框架首先将 XML 表示法解封为 String,然后调用 DateAdapter.unmarshal() 来将值类型调整为 Date。在编排过程中,JAXB 绑定框架调用 DateAdapter.marshal() 来将 Date 调整为 String,然后将其编排为 XML 表示。

4.3. Integrating via the JAXB Annotations

4.3.通过JAXB注解进行集成

The DateAdapter works like a plugin to JAXB and we’re going to attach it to our date field using the @XmlJavaTypeAdapter annotation. The @XmlJavaTypeAdapter annotation specifies the use of an XmlAdapter for custom unmarshalling:

DateAdapter就像JAXB的一个插件,我们将使用@XmlJavaTypeAdapter注解将其附加到我们的日期字段。@XmlJavaTypeAdapter注解指定使用XmlAdapter进行自定义解调

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

    // same as before

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

    // same as before

}

We’re also using the standard JAXB annotations: @XmlRootElement and @XmlElement annotations.

我们也在使用标准的JAXB注解@XmlRootElement@XmlElement 注解。

Finally, let’s run the new code:

最后,让我们运行新的代码。

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

5. Unmarshalling Dates in Java 8

5.在Java 8中取消对日期的排序

Java 8 introduced a new Date/Time API. Here, we’re going to focus on the LocalDateTime class which is one of the most commonly used.

Java 8 引入了一个新的Date/Time API。在这里,我们将重点讨论LocalDateTime类,它是最常用的一类。

5.1. Building a LocalDateTime-based XmlAdapter

5.1.建立一个基于LocalDateTimeXmlAdapter

By default, JAXB cannot automatically bind an xsd:dateTime value to a LocalDateTime object regardless of the date format. In order to convert an XML Schema date value to or from a LocalDateTime object, we need to define another XmlAdapter similar to the previous one:

默认情况下,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);
    }

}

In this case, we’ve used a DateTimeFormatter instead of a SimpleDateFormat. The former was introduced in Java 8 and it’s compatible with the new Date/Time API.

在这种情况下,我们使用了DateTimeFormatter而不是SimpleDateFormat前者是在Java 8中引入的,它与新的Date/Time API兼容。

Note that the conversion operations can share a DateTimeFormatter object because the DateTimeFormatter is thread-safe.

请注意,转换操作可以共享一个DateTimeFormatter对象,因为DateTimeFormatter是线程安全的

5.2. Integrating the New Adapter

5.2.整合新的适配器

Now, let’s replace the old adapter with the new one in our Book class and also Date with LocalDateTime:

现在,让我们在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

}

If we run the above code, we’ll get the output:

如果我们运行上述代码,我们会得到输出。

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

Note that the LocalDateTime.toString() adds the “T” delimiter between date and time.

注意,LocalDateTime.toString()在日期和时间之间添加了“T”分隔符。

6. Conclusion

6.结语

In this tutorial, we explored unmarshalling dates using JAXB.

在本教程中,我们探讨了使用JAXB解算日期

First, we looked at the XML Schema to Java data type mapping and created an example using the default XML Schema date format.

首先,我们看了XML Schema到Java数据类型的映射,并使用默认的XML Schema日期格式创建了一个例子。

Next, we learned how to use a custom date format based on a custom XmlAdapter and saw how to handle the thread safety of SimpleDateFormat.

接下来,我们学习了如何使用基于自定义XmlAdapter的自定义日期格式,并看到如何处理SimpleDateFormat的线程安全。

Finally, we leveraged the superior, thread-safe, Java 8 Date/Time API and unmarshalled dates with custom formats.

最后,我们利用了卓越的、线程安全的Java 8日期/时间API,以及带有自定义格式的非调用日期。

As always, the source code used in the tutorial is available over on GitHub.

一如既往,本教程中所使用的源代码可在GitHub上获得