Skip to content

Serialization and Deserialization of Kotlin data class fails on PolymorphicTypeValidator with Any #819

Open
@effx13

Description

@effx13

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

When trying to deserialize class that contains kotlin value class, Jackson throws InvalidTypeIdException with missing type id property '@class' on ObjectMapper.DefaultTyping.NON_FINAL
Despite adding @JsonCreator annotation, I got the same result.

And trying to serialize above class with ObjectMapper.DefaultTyping.NON_FINAL, Jackson throws JsonMappingException with class ServerName cannot be cast to class java.lang.String (ServerName is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap') (through reference chain: TestDto["serverName"])

I'm using PolymorphicTypeValidator and Any class to do serialization and deserialization in Redis. But no matter what settings and annotations I use, the serialization and deserialization fails.

Version Information

JVM 21
Kotlin 1.8

jackson-core:2.17.2
jackson-databind:2.17.2
jackson-annotations:2.17.2
jackson-datatype-jsr310:2.17.2
jackson-module-kotlin:2.17.2

Reproduction

@JvmInline
value class ServerName(
  val value: String,
) {
  companion object {
    @JsonCreator
    @JvmStatic
    fun fromValue(value: String): ServerName {
      return ServerName(value)
    }
  }
}

data class TestDto(
  val id: Int,
  val serverName: ServerName
)

val objectMapper = ObjectMapper()
  .registerKotlinModule()
  .registerModule(JavaTimeModule())
  .activateDefaultTyping(
    BasicPolymorphicTypeValidator.builder().allowIfBaseType(Any::class.java).build(),
    ObjectMapper.DefaultTyping.NON_FINAL, // or EVERYTHING
    JsonTypeInfo.As.PROPERTY,
  )

// on NON_FINAL
fun main() {
  val serverName = ServerName("TEST")
  val testDto = TestDto(1, serverName)

  val serialized = objectMapper.writeValueAsBytes(testDto)
  println(String(serialized))
  // Expected output: {"id":1,"serverName":"TEST"}
  // Actual output: {"id":1,"serverName":{"value":"TEST"}}

  val deserialized = objectMapper.readValue(serialized, Any::class.java) // Because of RedisSerializer
  println(deserialized)
  // Expected output: TestDto(id=1, serverName=ServerName(value=TEST))
  // Actual output: Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class'
}

// on EVERYTHING
fun main() {
  val serverName = ServerName("TEST")
  val testDto = TestDto(1, serverName)

  val serialized = objectMapper.writeValueAsBytes(testDto)
  println(String(serialized))
  // Expected output: {"id":1,"serverName":"TEST"}
  // Actual output: {"id":1,"serverName":{"value":"TEST"}}

  val deserialized = objectMapper.readValue(serialized, Any::class.java) // Because of RedisSerializer
  println(deserialized)
  // Expected output: TestDto(id=1, serverName=ServerName(value=TEST))
  // Actual output: Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: class ServerName cannot be cast to class java.lang.String (ServerName is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap') (through reference chain: TestDto["serverName"])
}

Expected behavior

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions