タイムスタンププロパティを持つモデルがあります。
class Model {
@JsonProperty("timestamp")
private OffsetDateTime timestamp;
}
タイムスタンプの形式は次のとおりです。
2017-09-17 13:45:42.710576+02
OffsetDateTime
はこれを解析できません:
com.fasterxml.jackson.databind.exc.InvalidFormatException:タイプ
Java.time.OffsetDateTime
の値を文字列 "2017-09-17 13:45:42.710576 + 02"からデシリアライズできません:テキスト '2017-09-17 13:45: 42.710576 + 02 'はインデックス10で解析できませんでした
どうすれば修正できますか?
日付の形式はジャクソンに伝えなければなりません。基本的には、year-month-day
の後にhour:minute:second.microseconds
が続き、2桁のオフセット(+02
)が続きます。したがって、パターンは次のようになります。
@JsonProperty("timestamp")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSSSSSx")
private OffsetDateTime timestamp;
より詳細な説明については すべての日付/時刻パターン を見てください。
OffsetDateTime
で同じオフセット(+02
)を保持する場合は、 DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE
オプション をfalse
に調整することを忘れないでください。
このオプションが(私のテストでは)true
に設定されている場合、結果はUTCに変換されます(ただし、実際には、Jacksonで構成されているタイムゾーンに変換されます)。
2017-09-17T11:45:42.710576Z
false
に設定すると、入力で使用されるオフセットが保持されます。
2017-09-17T13:45:42.710576 + 02:00
上記のコードは、小数点以下6桁で機能します。ただし、この数が異なる場合は、[]
で区切られたオプションのパターンを使用できます。
例:入力に6桁または3桁の10進数がある場合、pattern = "yyyy-MM-dd HH:mm:ss.[SSSSSS][SSS]x"
を使用できます。オプションのセクション[SSSSSS]
および[SSS]
は、パーサーに6桁または3桁を考慮するように指示します。
オプションのパターンの問題は、シリアル化するとすべてのパターンが印刷されることです(そのため、秒の小数部が2回印刷されます:6およびと3数字)。
別の方法は、カスタムシリアライザーとデシリアライザーを作成することです(com.fasterxml.jackson.databind.JsonSerializer
とcom.fasterxml.jackson.databind.JsonDeserializer
を拡張することにより):
public class CustomDeserializer extends JsonDeserializer<OffsetDateTime> {
private DateTimeFormatter formatter;
public CustomDeserializer(DateTimeFormatter formatter) {
this.formatter = formatter;
}
@Override
public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
return OffsetDateTime.parse(parser.getText(), this.formatter);
}
}
public class CustomSerializer extends JsonSerializer<OffsetDateTime> {
private DateTimeFormatter formatter;
public CustomSerializer(DateTimeFormatter formatter) {
this.formatter = formatter;
}
@Override
public void serialize(OffsetDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException {
gen.writeString(value.format(this.formatter));
}
}
次に、それらをJavaTimeModule
に登録できます。これを構成する方法は、使用している環境によって異なります(例:Springでは xml files で構成できます)。例として、プログラムでそれを実行します。
まず、Java.time.format.DateTimeFormatterBuilder
を使用してフォーマッタを作成します。
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// date/time
.appendPattern("yyyy-MM-dd HH:mm:ss")
// optional fraction of seconds (from 0 to 9 digits)
.optionalStart().appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).optionalEnd()
// offset
.appendPattern("x")
// create formatter
.toFormatter();
このフォーマッタは、0〜9桁のオプションの秒の小数部を受け入れます。次に、上記のカスタムクラスを使用して、それらをObjectMapper
に登録します。
// set formatter in the module and register in object mapper
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(OffsetDateTime.class, new CustomSerializer(formatter));
module.addDeserializer(OffsetDateTime.class, new CustomDeserializer(formatter));
mapper.registerModule(module);
また、フィールドから@JsonFormat
アノテーションを削除します。
@JsonProperty("timestamp")
private OffsetDateTime timestamp;
そして今、それは2017-09-17 13:45:42+02
(秒の小数部なし)や2017-09-17 13:45:42.71014+02
(5桁の10進数)などの値を受け入れます。 0から9桁の10進数(9はAPIでサポートされている最大値)を解析でき、シリアライズ時にまったく同じ数を出力します。
上記の代替方法は、カスタムクラスでフォーマッターを設定できるため、非常に柔軟です。ただし、すべてのOffsetDateTime
フィールドのシリアル化と逆シリアル化も設定します。
それが必要ない場合は、固定フォーマッタを使用してクラスを作成することもできます。
static class CustomDeserializer extends JsonDeserializer<OffsetDateTime> {
private DateTimeFormatter formatter = // create formatter as above
// deserialize method is the same
}
static class CustomSerializer extends JsonSerializer<OffsetDateTime> {
private DateTimeFormatter formatter = // create formatter as above
// serialize method is the same
}
次に、アノテーションcom.fasterxml.jackson.databind.annotation.JsonSerialize
およびcom.fasterxml.jackson.databind.annotation.JsonDeserialize
を使用して、必要なフィールドのみにそれらを追加できます。
@JsonProperty("timestamp")
@JsonSerialize(using = CustomSerializer.class)
@JsonDeserialize(using = CustomDeserializer.class)
private OffsetDateTime timestamp;
これにより、カスタムシリアライザーをモジュールに登録する必要がなくなり、注釈付きフィールドのみがカスタムクラスを使用します(他のOffsetDateTime
フィールドはデフォルト設定を使用します)。