jackson-module-kotlin
jackson-module-kotlin copied to clipboard
Consider supporting map key types that are value-type String wrappers
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
}
}
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).
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.
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
).
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.
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 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.
Got it, I have opened issue https://github.com/FasterXML/jackson-module-kotlin/issues/777.