1. 序章

このチュートリアルでは、JAXBを使用してさまざまな形式の日付オブジェクトを非整列化する方法を確認します。

最初に、デフォルトのスキーマ日付形式について説明します。 次に、さまざまな形式の使用方法について説明します。 また、これらの手法で発生する一般的な課題にどのように対処できるかについても説明します。

2. スキーマからJavaへのバインディング

まず、XMLスキーマとJavaデータ型の関係を理解する必要があります。 特に、XMLスキーマとJava日付オブジェクト間のマッピングに関心があります。

SchemaからJavaへのマッピングによると、考慮する必要のあるスキーマデータ型は xsd:date、xsd:time、xsd:dateTimeの3つです。 ご覧のとおり、これらはすべてjavax.xml.datatype.XMLGregorianCalendarにマップされています。

また、これらのXMLスキーマタイプのデフォルト形式を理解する必要があります。 The xsd:date xsd:time データ型には「 YYYY-MM-DD」 と “ hh:mm:ss」 フォーマット。 xsd:dateTime形式は「YYYY-MM-DDThh:mm:ss」です。ここで、「T」は時間セクションの開始を示す区切り文字です。

3. デフォルトのスキーマ日付形式の使用

日付オブジェクトを非整列化する例を作成します。 xsd:dateTime データ型は他の型のスーパーセットであるため、焦点を当てましょう。

本を説明する単純なXMLファイルを使用してみましょう。

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

ファイルを対応するJavaBookオブジェクトにマップします。

@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データを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);
}

上記のコードでは、JAXBAPIへのエントリポイントであるJAXBContextを定義しました。 次に、オブジェクトを読み取るために、入力ストリームでJAXB Unmarshallerを使用しました。

上記のコードを実行して結果を出力すると、次のBookオブジェクトが得られます。

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

xsd:dateTimeのデフォルトのマッピングはXMLGregorianCalendar ですが、より一般的なJavaタイプ java.util.Date[ JAXBユーザーガイドによるとおよびjava.util.Calendar

4. カスタム日付形式の使用

上記の例は、デフォルトのスキーマ日付形式「YYYY-MM-DDThh:mm:ss」を使用しているために機能します。

しかし、「YYYY-MM-DDhh:mm:ss」、 「T」区切り文字を削除するなど、別の形式を使用する場合はどうでしょうか。 XMLファイルで区切り文字をスペース文字に置き換えると、デフォルトのアンマーシャリングは失敗します。

4.1. カスタムXmlAdapterの構築

別の日付形式を使用するには、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);
    }

}

このアダプターでは、 使用しました SimpleDateFormatを使用して、日付をフォーマットします。 私たちは注意する必要があります SimpleDateFormatはスレッドセーフではありません。 複数のスレッドで共有の問題が発生しないようにするため SimpleDateFormat オブジェクト、必要になるたびに新しいオブジェクトを作成しています。

4.2. XmlAdapterの内部

ご覧のとおり、 XmlAdapterには2つのタイプパラメータがあります。この場合、StringDateです。 1つ目は、XML内で使用される型であり、値型と呼ばれます。 この場合、JAXBはXML値をStringに変換する方法を知っています。 2つ目はバインドされた型と呼ばれ、Javaオブジェクトの値に関連しています。

アダプターの目的は、JAXBがデフォルトでは実行できない方法で、値型とバインドされた型の間で変換することです。

カスタムXml Adapter を作成するには、 XmlAdapter.marshal() XmlAdapter.unmarshal()[X150Xの2つのメソッドをオーバーライドする必要があります。 ]。

アンマーシャリング中に、JAXBバインディングフレームワークは最初にXML表現を String にアンマーシャリングし、次に DateAdapter.unmarshal()を呼び出して値型をDateに適合させます。 マーシャリング中に、JAXBバインディングフレームワークは DateAdapter.marshal()を呼び出して、DateStringに適合させ、XML表現にマーシャリングします。

4.3. JAXBアノテーションを介した統合

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

}

また、標準のJAXBアノテーション@XmlRootElementおよび@XmlElementアノテーションも使用しています。

最後に、新しいコードを実行してみましょう。

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

5. Java8での日付の非整列化

Java 8では、新しい Date / TimeAPIが導入されました。 ここでは、最も一般的に使用されるLocalDateTimeクラスに焦点を当てます。

5.1. LocalDateTimeベースのXmlAdapterの構築

デフォルトでは、 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);
    }

}

この場合、SimpleDateFormatの代わりにDateTimeFormatterを使用しました。前者はJava8で導入され、新しい Date / TimeAPIと互換性があります。

DateTimeFormatterはスレッドセーフであるため、変換操作はDateTimeFormatterオブジェクトを共有できることに注意してください。

5.2. 新しいアダプタの統合

それでは、古いアダプターを Book クラスの新しいアダプターに置き換え、DateLocalDateTimeに置き換えましょう。

@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のスレッドセーフを処理する方法を確認しました。

最後に、スレッドセーフな優れたJava 8 Date / Time APIと、カスタム形式のマーシャリングされていない日付を活用しました。

いつものように、チュートリアルで使用されるソースコードは、GitHubから入手できます。