When deserializing a map with Long keys from JSON using an jackson objectmapper, the Long keys are being deserialized as Strings

65 views Asked by At
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.KotlinModule
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class ObjectMapperTest {

    @Test
    fun hello() {
        val objectMapper = objectMapper()

        val map: Map<Long, String> = objectMapper.readValue(objectMapper.writeValueAsString(mapOf(1L to "hi", 2L to "hello")), Map::class.java) as Map<Long, String>
        assertThat(map[1L]).isEqualTo("hi")
    }

    private fun objectMapper(): ObjectMapper {
        val objectMapper = ObjectMapper()
            .registerModule(JavaTimeModule())
            .registerModule(KotlinModule())
            .activateDefaultTyping(
                BasicPolymorphicTypeValidator.builder().allowIfBaseType(Any::class.java).build(),
                ObjectMapper.DefaultTyping.EVERYTHING
            )

        return objectMapper
    }

}

As i already found that json key must be String type from this post , but i want to make this test code to be passed. Is there any Jackson ObjectMapper configurations to apply? I already tried some options or make custom KeyDeserializer but failed. How can i fix it?

Exactly, i want to use object mapper as a serializer/deserializer for redis cache. So it impelements RedisSerializer and i have to use Generic and Type Reference. So using jacksonTypeRef() doesn't work!

override fun deserialize(bytes: ByteArray?): T? {
    if (bytes == null) {
        return null
    }

    if (!isGzipCompressed(bytes)) {
        return objectMapper.readValue(bytes, jacksonTypeRef()) // Cannot use 'T' as reified type parameter. Use a class instead.
        // return objectMapper.readValue(bytes, object : TypeReference<T>() {}) doesn't work
    }

    try {
        val byteArrayHolder = decodeGzip(bytes)
        return objectMapper.readValue(byteArrayHolder.rawByteArray, 0, byteArrayHolder.limit, object : TypeReference<T>() {})
    } catch (ex: IOException) {
        throw IllegalStateException("couldn't encode body to gzip", ex)
    }
}
1

There are 1 answers

5
dnault On

Specify the map key type using a type reference.

The duplicate question I suggested is for Java. Kotlin has slightly different syntax, which looks like:

val map: Map<Long, String> = objectMapper.readValue(
    """{"1":"a","2":b"}""",
    jacksonTypeRef(),
)

or

val map = objectMapper.readValue(
    """{"1":"a","2":b"}""",
    jacksonTypeRef<Map<Long, String>>(),
)