torutkのブログ

ソフトウェア・エンジニアのブログ

JAXBでCalendarを任意の書式でXMLに出す

JAXBでJavaクラスからXML schema生成 - torutkの日記の続きです。

Javaのクラスでjava.util.Calendar型のデータをJAXBでXMLに出力すると、XML Schema上はxs:dateTimeにマッピングされます。
具体的な出力形式は、

dateTime="2011-10-01T23:00:00.261+09:00"

のように、年-月-日T時:分:秒.ミリ秒+タイムゾーン(オフセット) という書式となります。
Calendar型を、日付として使っている場合、あるいは日時としているが秒、ミリ秒、タイムゾーンは使用しない場合、上述の形式はちょっと不便です。

XML上で日時の形式を自由に設定したいところですが、その方法が見当たりませんでした。

そこで、JavaのクラスのCalendar型をXML上では文字列(xs:string)にマッピングするようにJAXBアノテーションで指定し、Calendar型と文字列を変換するアダプターを作成します。

まず、昨日のMeasureクラスにアノテーションを追加します。

    @XmlAttribute
    @XmlJavaTypeAdapter(XmlDateAdapter.class)
    private Calendar dateTime;

@XmlJavaTypeAdapterアノテーションを追加し、作成するアダプタークラス(後述)を指定します。

アダプタークラスXmlDateAdapterを作成します。

package jp.gr.java_conf.torutk.weather;

import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.util.Calendar;
import java.util.Date;
import java.text.SimpleDateFormat;

public class XmlDateAdapter extends XmlAdapter<String, Calendar> {
    private SimpleDateFormat dateFormat = new SimpleDateFormat(
        "yyyy-MM-dd HH:mm:ss"
    );
    
    @Override
    public Calendar unmarshal(String xmlDateTime) throws Exception {
        Date date = dateFormat.parse(xmlDateTime);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal;
    }

    @Override
    public String marshal(Calendar calendar) throws Exception {
        return String.format("%1$tF %1$tT", calendar);
    }
}

アダプタークラスは、XmlAdapterを継承して作成します。
型パラメータValueTypeはJAXBが対応可能な型、BoundTypeはこのアダプターでJAXB対応可能な型と結び付ける(通常はユーザー定義の)型です。

  • SimpleDateFormatはマルチスレッドセーフでないので、フィールドではなくローカル変数で定義すべきかもしれません。JAXBがXmlAdapterをマルチスレッドで呼ぶ可能性があるかは調査していません。

このクラスを追加してコンパイルし、実行すると

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:weather xmlns:ns2="http://java-conf.gr.jp/torutk/weather">
    <measures>
        <measure dateTime="2011-10-01 22:00:00" windSpeed="3" windDirection="122" humidity="55.0" temperature="21.5"/>
        <measure dateTime="2011-10-01 23:00:00" windSpeed="2" windDirection="140" humidity="58.0" temperature="20.8"/>
    </measures>
</ns2:weather>

と日付の書式がアダプターで指定した形になりました。

  • あれ、なぜかweatherに名前空間が追加されるようになった。不思議だ・・・