question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Jackson configuration is cumbersome

See original GitHub issue

I need to register jackson-module-kotlin with the Temporal SDK in order to support Kotlin data classes. As far as Jackson goes, this is accomplished with a fairly trivial modification to the ObjectMapper. However, the Temporal SDK currently makes it very cumbersome to get a hook onto that reference.

While it’s possible to declare your entirely own DataConverter, it requires replicating a large portion of the default Temporal configuration. E.g., here is the code I would need to write if I just wanted to make one simple Jackson modification:

// Replicate the default Jackson configuration
val mapper = ObjectMapper()
mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false)
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
mapper.registerModule(JavaTimeModule())
mapper.registerModule(Jdk8Module())
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)

// Enable my Jackson customization
mapper.registerKotlinModule()

// Replicate the default payload converters (in order) while overriding JacksonJsonPayloadConverter
val dataConverter = DefaultDataConverter(
    NullPayloadConverter(),
    ByteArrayPayloadConverter(),
    ProtobufJsonPayloadConverter(),
    ProtobufPayloadConverter(),
    JacksonJsonPayloadConverter(mapper)
)

// Register the global default
GlobalDataConverter.register(dataConverter)

This is a lot of boilerplate and prone to falling out of sync with the defaults that we’re copy/pasting.

Alternatively, I can use reflection to modify the ObjectMapper that is already registered globally. E.g.,

val jacksonConverter = DefaultDataConverter.STANDARD_PAYLOAD_CONVERTERS.first { it is JacksonJsonPayloadConverter }
val mapperField = JacksonJsonPayloadConverter::class.java.getDeclaredField("mapper")
mapperField.isAccessible = true
val mapper = mapperField.get(jacksonConverter) as ObjectMapper
mapper.registerKotlinModule()

This is of course hacky, but I actually prefer it since I know my settings won’t drift out of sync from Temporal’s default configuration, and I’ll get an explicit failure if something breaks with the reflection.

It would be nice if it was simpler and easier to be able to customize the Jackson-level configuration. Some potential solutions:

  1. Similar to exposing a global DataConverter, expose a global ObjectMapper
  2. Expose properties for List<PayloadConverter> and ObjectMapper that would allow a user to drill down into the current mapper to modify it (this still feels a little hacky, since I would rather treat the current mapper as immutable)
  3. Expose something like a UnaryOperator<ObjectMapper> that allows a user to customize the mapper alongside Temporal’s existing default configuration
  4. Expose an environment variable that will trigger Temporal to call mapper.registerKotlinModule()
  5. Expose an environment variable that will trigger Temporal to call mapper.findAndRegisterModules()

Related:

  1. https://community.temporal.io/t/hi-deserialization-problem/474/4
  2. https://github.com/temporalio/sdk-java/issues/139

Version: 1.16.0

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:8 (4 by maintainers)

github_iconTop GitHub Comments

0reactions
Bennett-Lynchcommented, Aug 27, 2022

Offering automatic configuration via class-path module detection is a very convenient utility. Without it, I’ll now be littering any project using Temporal and Kotlin with manual overrides in both prod/test configuration (which are somewhat cumbersome to insert, due to their global nature). In my case, I’m perfectly happy with the default serialization configuration otherwise. If including such detection in temporal-kotlin is too risky, I personally would love to have a separate and dedicated module option, like temporal-kotlin-jackson. Just my 2 cents, but I respect your opinion as the maintainer.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Allow to configure line endings (and probably indentation) #585
When writing multi-OS unit tests, it's cumbersome to filter the line endings from Jackson's output. Please add a config option which allows ...
Read more >
Should I declare Jackson's ObjectMapper as a static field?
Yes, that is safe and recommended. The only caveat from the page you referred is that you can't be modifying configuration of the...
Read more >
Jackson 2.9 features - cowtowncoder
Property names just needs to exist as empty List ; we could have either used SKIP as above, or force it to be...
Read more >
Deserialize Immutable Objects with Jackson - Baeldung
In this quick tutorial, we'll show two different ways of deserializing immutable Java objects with the Jackson JSON processing library.
Read more >
Three ways to use Jackson for JSON in Java - Twilio
Jackson uses a class called ObjectMapper as its main entrypoint. ... This is a little awkward - Jackson does not directly allow us...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found