Конфигурация 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 часть для получения дополнительной информации.

Другие вопросы по тегам