I have a message being received through Kafka, which I know has a non-UTC timezone in it.
When I use the org.apache.kafka.common.serialization.StringDeserializer
to verify this I get the right timestamp in ISO 8601 format with the timezone:
{ "id": "e499f2e8-a50e-4ff8-a9fe-0eaf9d3314bf", "sent_ts": "2021-02-04T14:06:10+01:00" }
When I switch to the org.springframework.kafka.support.serializer.JsonDeserializer
this is lost. My POJO looks like this:
public class MyMessage {
@JsonProperty("id")
private String id;
@JsonProperty("sent_ts")
private OffsetDateTime sentTs;
@Override
public String toString() {
return "MyMessage{" +
"id='" + id + '\'' +
", sentTs=" + sentTs +
'}';
}
When I log the message I receive I get:
MyMessage{id='e499f2e8-a50e-4ff8-a9fe-0eaf9d3314bf', sentTs=2021-02-04T13:06:10Z}
I thought the JsonDeserializer
must be using Jackson so in my application.yml
configuration I set:
spring.jackson:
deserialization.ADJUST_DATES_TO_CONTEXT_TIME_ZONE: false
This didn't work. I also tried to a customizer:
@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);
}
}
Which didn't work either.
I though maybe it needs to be a property of the Kafka consumer, so I also tried:
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
Still doesn't work.
Is there a way to make the JsonDeserializer
work properly and keep the correct timezone offset?
When you do like this value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
, the instance of that class is created by Apache Kafka client code which is fully not aware of Spring configuration.
If you'd like to rely on the ObjectMapper
configured by Spring Boot and your customizations, you should consider to do something like this:
@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);
}
Pay attention how I call jsonDeserializer.configure(consumerProperties, false);
. This way you still will be able to configure the rest of properties for Kafka consumer in the applicaiton.yml
.
Please, consider to raise GH issue for Spring Boot, so we will revise how we deal with JsonDeserializer
and auto-configured ObjectMapper
to server better end-user experience.