Джексон использует InstantDeserializer вместо зарегистрированного CustomDeserializer для десериализации OffsetDateTime в Quarkus

Фронтенд отправляет даты в формате: 2024-06-01T05:55:04 от datetime-local control.

Пример контроллера REST:

@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ExampleResource {
  @POST
  public RestResponse<Example> create(Example example) {
    return RestResponse.ok(exampleService.create(example));
  }
}

@Data
public class Example {
    @JsonProperty("creationTime")
    private OffsetDateTime creationTime;  // from front-end comes 2024-06-01T05:55:04
}

Здесь добавлен собственный десериализатор для OffsetDateTime:

@Singleton
public class ObjectMapperConfig implements ObjectMapperCustomizer {

  public void customize(ObjectMapper mapper) {
    var module = new SimpleModule();
    module.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer());
    mapper.registerModule(module);
  }

  static class CustomOffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> {

    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");

    // figured out that this method is not called during deserialization
    @Override
    public OffsetDateTime deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
      String dateString = p.getText();
      try {
        return OffsetDateTime.parse(dateString);
      } catch (Exception e) {
        var localDateTime = LocalDateTime.parse(dateString, FORMATTER);
        return localDateTime.atOffset(ZoneOffset.systemDefault().getRules().getOffset(localDateTime));
      }
    }

  }

}

Я попробовал другой подход - переопределить bean-компонент ObjectMapper по умолчанию:

  @Produces
  @Singleton
  public ObjectMapper objectMapper() {
    var module = new SimpleModule();
    module.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer());
    var objectMapper = new ObjectMapper();
    objectMapper.registerModule(module);
    return objectMapper;
  }

Ничего из вышеперечисленного не работает, и Quarkus не может десериализовать даты. Браузер получает ответ HTTP 400 Bad Request без тела.

Я запустил отладчик и понял, что InstantDeserializer по умолчанию используется для десериализации OffsetDateTime.

Как правильно настроить собственный десериализатор для OffsetDateTime?

Должен ли я использовать другой подход для анализа дат, например от 2024-06-01T05:55:04 до OffsetDateTime?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Наконец-то создал миксин, и он работает.

@Singleton
public class ObjectMapperConfig implements ObjectMapperCustomizer {

  public void customize(ObjectMapper mapper) {
    mapper.addMixIn(OffsetDateTime.class, DateMixin.class); // add mixin
  }

  // This mixin will be used to deserialize OffsetDateTime
  @JsonDeserialize(converter = CustomOffsetDateTimeConverter.class)
  static class DateMixin {
  }

  // Define a converter from String to OffsetDateTime
  static class CustomOffsetDateTimeConverter extends StdConverter<String, OffsetDateTime> {

    // We will try to parse this format on error
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");

    @Override
    public OffsetDateTime convert(String dateString) {
      try {

        // First try to parse as OffsetDateTime
        return OffsetDateTime.parse(dateString);

      } catch (Exception e) {

        // Then continue to parse using our format (without offset)
        var localDateTime = LocalDateTime.parse(dateString, FORMATTER);

        // Now use system default timezone to convert local datetime to offset datetime
        return localDateTime.atOffset(ZoneOffset.systemDefault().getRules().getOffset(localDateTime));

      }
    }
  }

}

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