【问题标题】:How to make Spring Kafka JsonDeserializer retain the timezone offset when deserializing to OffsetDateTime反序列化为 OffsetDateTime 时如何使 Spring Kafka JsonDeserializer 保留时区偏移量
【发布时间】:2026-02-17 07:50:01
【问题描述】:

我通过 Kafka 收到一条消息,我知道其中包含非 UTC 时区。 当我使用org.apache.kafka.common.serialization.StringDeserializer 验证这一点时,我得到了带有时区的 ISO 8601 格式的正确时间戳:

{  "id": "e499f2e8-a50e-4ff8-a9fe-0eaf9d3314bf", "sent_ts": "2021-02-04T14:06:10+01:00" }

当我切换到org.springframework.kafka.support.serializer.JsonDeserializer 时,它会丢失。我的 POJO 是这样的:

public class MyMessage {

    @JsonProperty("id")
    private String id;

    @JsonProperty("sent_ts")
    private OffsetDateTime sentTs;

    @Override
    public String toString() {
        return "MyMessage{" +
                "id='" + id + '\'' +
                ", sentTs=" + sentTs +
                '}';
}

当我记录收到的消息时,我得到:

MyMessage{id='e499f2e8-a50e-4ff8-a9fe-0eaf9d3314bf', sentTs=2021-02-04T13:06:10Z}

我认为JsonDeserializer 必须使用Jackson,所以在我设置的application.yml 配置中:

spring.jackson:
    deserialization.ADJUST_DATES_TO_CONTEXT_TIME_ZONE: false

这不起作用。我也试过定制器:

@Configuration
public class ObjectMapperBuilderCustomizer implements Jackson2ObjectMapperBuilderCustomizer {

    @Override
    public void customize(Jackson2ObjectMapperBuilder builder) {
        builder.modules(new JavaTimeModule());
        builder.featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
    }
}

这也不起作用。

虽然可能它需要成为 Kafka 消费者的属性,所以我也尝试了:

spring:
    consumer:
      auto-offset-reset: earliest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      properties:
        spring.jackson.deserialization.ADJUST_DATES_TO_CONTEXT_TIME_ZONE: false

还是不行。

有没有办法让JsonDeserializer 正常工作并保持正确的时区偏移?

【问题讨论】:

    标签: java spring-boot jackson spring-kafka offsetdatetime


    【解决方案1】:

    当您这样做value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer 时,该类的实例由完全不了解 Spring 配置的 Apache Kafka 客户端代码创建。

    如果你想依赖 Spring Boot 配置的 ObjectMapper 和你的自定义,你应该考虑做这样的事情:

    @Bean
    DefaultKafkaConsumerFactory kafkaConsumerFactory(KafkaProperties properties, ObjectMapper objectMapper) {
       Map<String, Object> consumerProperties = properties.buildConsumerProperties();
       JsonDeserializer<Object> jsonDeserializer = new JsonDeserializer<>(objectMapper);
       jsonDeserializer.configure(consumerProperties, false);
    
       return new DefaultKafkaConsumerFactory(consumerProperties, 
                       new StringDeserializer(), jsonDeserializer);
    }
    

    注意我如何称呼jsonDeserializer.configure(consumerProperties, false);。这样,您仍然可以在 applicaiton.yml 中为 Kafka 消费者配置其余属性。

    请考虑为 Spring Boot 提出 GH 问题,因此我们将修改处理 JsonDeserializer 和自动配置 ObjectMapper 的方式,以提供更好的最终用户体验。

    【讨论】:

    • 这看起来不错,但随后JsonDeserializer 会忽略配置,如json 默认类型,来自application.yml,对吗?
    • 没错,但我不确定使用 JsonDeserializer 类的设置器有什么问题...
    • 如果您在 yaml 文件中设置了设置并且它们不起作用,那将会很混乱。有没有办法在加载正确配置的情况下获取默认的JsonDeserializer,然后更改其ObjectMapper 配置?我只需要关闭这个时区偏移量。
    • 嗯,对我来说,在 yaml 中配置 Java 对象更令人困惑。我真希望我们不要一开始就引入这样的配置……通过 Java 代码配置对象确实看起来更自然,尤其是在它们之间存在依赖关系的情况下。
    • 最后我想你可以在这个JsonDeserializer上调用configure(properties.buildConsumerProperties(), false)来模拟Kafka客户端逻辑。