Конфигурация ObjectMapper для Java 8 Time API
Мы переходим с Joda на Java Time. В настоящее время мы используем DateTime
Йода в нашей сущности. насколько мне известно DateTime
эквивалентно двум типам в Java: OffsetDateTime
а также ZonedDateTime
, Так как мы собираемся сохранить их в БД, мы будем использовать OffsetDateTime
(любой комментарий по этому поводу?).
Теперь проблема в том, как настроить Джексона ObjectMapper
должным образом. Все примеры, которые я нашел в Интернете, относятся к локальным типам, для которых Джексон уже предоставил реализации de/serializer (например, LocalDateTime
, LocalDateTimeSerializer
а также LocalDateTimeDeserializer
).
Мне наконец удалось сделать что-то вроде этого:
public class OffsetDateTimeSerializer extends StdSerializer<OffsetDateTime> {
private final DateTimeFormatter formatter; // We need custom format!
public OffsetDateTimeSerializer(DateTimeFormatter formatter) {
super(OffsetDateTime.class);
this.formatter = formatter;
}
@Override
public void serialize(OffsetDateTime value, JsonGenerator generator, SerializerProvider provider) throws IOException {
generator.writeString(value.format(formatter));
}
}
а также
public class OffsetDateTimeDeserializer extends StdDeserializer<OffsetDateTime> {
private final DateTimeFormatter formatter; // We need custom format!
public OffsetDateTimeDeserializer(DateTimeFormatter formatter) {
super(OffsetDateTime.class);
this.formatter = formatter;
}
@Override
public OffsetDateTime deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
return OffsetDateTime.parse(parser.readValueAs(String.class), formatter);
}
}
Теперь мой вопрос, каков наилучший способ настроить Джексона ObjectMapper
де / сериализовать значения даты и времени Java 8?
ОБНОВЛЕНИЕ: принятый ответ действительно не решает мою проблему (читайте обсуждение в комментариях). Я закончил с немного более простым кодом, чем то, что я предложил выше. Смотрите также мой собственный ответ.
3 ответа
Вам не нужно писать свой собственный сериализатор и десериализатор для типов JSR-310. У Джексона есть специальный модуль для этого, и он предоставит вам сериализатор и десериализатор, которые вам нужны.
Сначала добавьте jackson-datatype-jsr310
Артефакт для ваших зависимостей:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9</version>
</dependency>
Затем зарегистрируйте JavaTimeModule
модуль в вашем ObjectMapper
:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
Большинство типов JSR-310 будут сериализованы с использованием стандартного строкового представления ISO-8601. Если вам нужен собственный формат, вы можете использовать свой собственный сериализатор и десериализатор.
Смотрите документацию для деталей.
Итак, я получил следующее (немного меньше кода, никаких конкретных классов):
private JavaTimeModule newJavaTimeModule() {
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(LocalDate.class, new LocalDateSerializer(DEFAULT_LOCAL_DATE_FORMATTER));
module.addDeserializer(LocalDate.class, new LocalDateDeserializer(DEFAULT_LOCAL_DATE_FORMATTER));
module.addSerializer(OffsetDateTime.class, offsetDateTimeSerializer());
module.addDeserializer(OffsetDateTime.class, offsetDateTimeDeserializer());
return module;
}
private StdSerializer<OffsetDateTime> offsetDateTimeSerializer(DateTimeFormatter formatter) {
return new OffsetDateTimeSerializer(OffsetDateTimeSerializer.INSTANCE, false, formatter) {};
}
private StdDeserializer<OffsetDateTime> offsetDateTimeDeserializer(DateTimeFormatter formatter) {
return new InstantDeserializer<OffsetDateTime>(InstantDeserializer.OFFSET_DATE_TIME, formatter) {};
}
Вы можете проверить этот ответ, который содержит много информации о том, как использовать классы java.time и пользовательские форматы: /questions/21533166/dzhekson-razobrat-polzovatelskoe-smeschenie-datyi-i-vremeni/21533167#21533167
Чтобы проанализировать "+00:00" и "+0000", вы можете использовать DateTimeFormatterBuilder
с дополнительными разделами:
DateTimeFormatter f = new DateTimeFormatterBuilder()
// date and time fields
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
// optional offset in format hh:mm
.optionalStart()
.appendOffset("+HH:MM", "+00:00")
.optionalEnd()
// optional offset in format hhmm
.optionalStart()
.appendOffset("+HHMM", "+0000")
.optionalEnd()
.toFormatter();
Spring boot осуществляет управление зависимостями для Джексона. В версии 2.6 модуль jsr310 может управляться автоматически, поэтому вы просто используете Jackson 2.6+ и добавляете модуль, который доступен в com.fasterxml.jackson.datatype:jackson-datatype-jsr310
,
Если у вас есть доступ к ObjectMapper
, например, в модульном тесте, вы можете зарегистрировать JavaTimeModule
, Если вы этого не сделаете, то, что происходит, когда JSON приходит как @RequestBody
, вы должны настроить в application.properties:
spring.jackson.serialization.write-dates-as-timestamps=false
И укажите формат даты в формате JSON, например:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXXX")
private OffsetDateTime transactionDateTime;
И строка будет проанализирована правильно. В формате используются буквы, указанные в SimpleDateFormat
,
Следите за количеством X
в конце; различная длина которого представляет различные формы смещения зоны. Читать Java API SimpleDateFormat
часть для получения дополнительной информации.