jackson-module-kotlin icon indicating copy to clipboard operation
jackson-module-kotlin copied to clipboard

Consider supporting map key types that are value-type String wrappers

Open MrBuddyCasino opened this issue 2 years ago • 3 comments

It would be nice to have support for stringly-typed values, such as @JvmInline value class SKU(val value: String). Writing custom map key deserializers for every custom Kotlin value type that is really just a String doesn't seem ergonomic.

Currently this throws a java.lang.IllegalArgumentException: Cannot find a (Map) Key deserializer for type [simple type, class ValueTypeTest$SKU], repro case:

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.convertValue
import org.junit.jupiter.api.Test

class ValueTypeTest {

    private val mapper =  ObjectMapper().registerModule(KotlinModule.Builder().build())

    @JvmInline
    value class SKU(val value: String)

    data class FavoritesEntity(
        var favorites: LinkedHashMap<SKU, Int>
    )

    @Test
    fun test() {
        val entity = FavoritesEntity(favorites = linkedMapOf(SKU("milk") to 1))
        val json = mapper.writeValueAsString(entity)
        val deserialized = mapper.convertValue<FavoritesEntity>(json)
        deserialized.favorites
    }

}

MrBuddyCasino avatar Aug 05 '22 14:08 MrBuddyCasino

So just to confirm I understand this: it'd be good to have special handling for Value types that consists of a single String property? Yes, I can see how that could be useful. And I suspect same would go for Java 17 record types with a single String property, for what that is worth.

I won't be able to help on Kotlin side (since I don't know Kotlin that well) but I hope someone else might want to tackle this, via added KeyDeserializers implementation (and probably matching functionality for key serializers).

cowtowncoder avatar Aug 09 '22 22:08 cowtowncoder

Yes thats what I meant. Possibly also Data Classes with a single String property, though I expect this to be a much rarer use case.

Value classes can be used as a way to add type safety without overhead. Instead of a String arg, define a value class and now you cannot put the customer number where the order number is supposed to go. On the JVM level this should impose no additional allocations or indirections.

Kotlin also has type aliases, but those do not add type safety as they are assignment-compatible with their aliases, and thus they already work with Jackson.

If you point me in the right direction I might be able to help with the Kotlin side.

MrBuddyCasino avatar Aug 10 '22 09:08 MrBuddyCasino

I hope module maintainers can help here, unfortunately I don't really know the codebase. But I think there may already be key serializers/deserializers added via KotlinModule. These are separate from "value" serializers and deserializers, either by type (KeyDeserializer) or just functionality (key serializers do implement same class as value type, but need to use different method to write key -- JsonToken.FIELD_NAME vs JsonToken.VALUE_STRING).

cowtowncoder avatar Aug 10 '22 17:08 cowtowncoder

Serialization is already supported with the exception of a few features (#536). Deserialization is covered in #650.

Therefore, this issue is closed as a duplicate.

k163377 avatar Mar 21 '23 11:03 k163377

Deserializing a map where the key is an value class does not work in 2.17 unfortunately:

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper

@JvmInline
value class Id(val value: Int)

data class Foo(val valuesById: Map<Id, String>)

val objectMapper = jacksonObjectMapper()

val serialized = objectMapper.writeValueAsString(Foo(mapOf(Id(1) to "one", Id(2) to "two")))

val deserialized = objectMapper.readValue(serialized, Foo::class.java)

Throws:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot find a (Map) Key deserializer for type...

geirsagberg avatar Mar 17 '24 13:03 geirsagberg

@geirsagberg If there are issues remaining, please file a new issue outlining specific problem. It may (and probably should) reference this issue as background, but we do not typically re-open old issues unless it's part of on-going development of feature for branch from which there is no release yet.

cowtowncoder avatar Mar 21 '24 01:03 cowtowncoder

Got it, I have opened issue https://github.com/FasterXML/jackson-module-kotlin/issues/777.

geirsagberg avatar Mar 21 '24 06:03 geirsagberg