Transient saga resources not deserializing correctly

Hi,
We are in the process of upgrading our Springboot applications that run Axon to JDK17. As has been discussed in other posts, this seems to cause issues with the default XStream deserializer. To fix this, I switched to Jackson, which resolved all of my domain event deserializing problems. However, at this point, our sagas starting throwing errors when attempting to deserialize. I tried tweaking my ObjectMapper by adding some additional settings and configs, but this didn’t solve anything. I finally resolved the issue by removing @Transient from our resource definitions, and adding @com.fasterxml.jackson.annotation.JsonIgnore instead. My question is around the support of @Transient - is it intended to only be used with the XStream deserializer?

A few notes:

  • we are using MongoDb as our storage engine, in case it matters
  • the saga resources are autowired in via Springboot’s Bean management system, which is I think what allows me to get away with just ignoring the field via deserialization.

Upon further testing, I have also found that any state variables on the saga are not coming back upon Saga deserialization. My sagas are breaking because of missing lazy variables.

I have seen @Gerard weighing in on the topic of Jackson deserializers on a separate thread. Would you be able to comment on this? Are there some additional settings that I need to adjust?

For reference here is my serializer definition (our codebase is Kotlin):

    @Primary
    @Bean
    fun jacksonSerializer(): JacksonSerializer {
        val mapper = ObjectMapper()
            .registerModule(
                KotlinModule.Builder()
                    .withReflectionCacheSize(512)
                    .configure(KotlinFeature.NullToEmptyCollection, false)
                    .configure(KotlinFeature.NullToEmptyMap, false)
                    .configure(KotlinFeature.NullIsSameAsDefault, false)
                    .configure(KotlinFeature.SingletonSupport, false)
                    .configure(KotlinFeature.StrictNullChecks, false)
                    .build(),
            )
            .setSerializationInclusion(JsonInclude.Include.NON_NULL)
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
        mapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false)
        return JacksonSerializer
            .builder()
            .objectMapper(mapper)
            .lenientDeserialization()
            .build()
    }

Ok, I was able to resolve the state variables not being deserialized by adding @com.fasterxml.jackson.annotation.JsonProperty to all the variables. This seems like a straightforward approach.

I do wonder what you are using the @Transient for. Most things should be able to get via parameter resolvers in the function, and doesn’t need to be on the saga itself. And if there are marked @Transient you likely don’t want to serialize them?

Ah, looking back at the documentation for Sagas, I see the recommendation was for the transient keyword, not annotation. That’s a mistake on our part. In any case, I removed that annotation since it was specifically causing issues. I still had to add @JsonIgnore in its place for the deserialization to work properly with Jackson.

What I find most unusual is that, in other places in our application, we use Jackson’s ObjectMapper to (de)serialize data. None of our models require that we add the annotations - it just works. So what is it about the JacksonSerializer implementation that requires the annotations to be present. And is it strictly necessary that they be required? (not criticizing, just asking the question :slightly_smiling_face:).

There are several ways with Jackson. Typically if it’s a private field, without public getter it won’t be serialized. There are ways around with annotations, with configuration to the object mapper, and likely others. Since the typical message will be created from a pojo, those will be serialized without annotations or customizations to the object mapper.