jackson-module-kotlin
jackson-module-kotlin copied to clipboard
Cannot use value class as map key in 2.17
Search before asking
- [X] I searched in the issues and found nothing similar.
- [X] I searched in the issues of databind and other modules used and found nothing similar.
- [X] I have confirmed that the problem only occurs when using Kotlin.
Describe the bug
Jackson 2.17 introduced support for deserializing value classes. However, using a value class as a key in a map still fails on deserialization.
To Reproduce
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...
Expected behavior
When deserializing a map Map<MyValueClass, Any>
where MyValueClass
wraps a supported primitive, the map should deserialize without throwing an error.
Versions
Kotlin: 1.9.22 Jackson-module-kotlin: 2.17.0 Jackson-databind: 2.17.0
Additional context
No response
I am positively considering adding this feature.
I have made a prototype at kogera
for policy consideration.
https://github.com/ProjectMapK/jackson-module-kogera/pull/224
It will be basically the same usability as deserialization of value class
.
The difference is that the wrapped type is also deserialized by the context-accessible KeyDeserializer
.
However, I am busy and sick, so it may take some time until I can merge it into kotlin-module
.
I hope to complete it by May at the latest.
@cowtowncoder
I have two questions about Jackson
functionality.
I don't believe there is a JsonCreator
like feature used to deserialize keys, is this correct?
Also, are there any caveats about searching for another KeyDeserializer
from a KeyDeserializer
?
https://github.com/ProjectMapK/jackson-module-kogera/pull/224/files#diff-b03a6f91081da3aa22c53b161b0a2f6f1a0cf4ba318329907046b5f5335b17c8R72-R73
I am assuming that this is a very limited edge case, but I am assuming that there are nested types that require a special deserialization method. https://github.com/ProjectMapK/jackson-module-kogera/pull/224/files#diff-11c6e0b61e487e3914690655846ffd6100b92c022082ea610bb8d086f7be18dcR87-R116
Just to clarify, previous versions of jackson don't exhibit errors when using value classes as keys in maps. As such, I think this should be labeled as a bug and not an enhancement.
@junkdog
A generic key deserializer for the value class
is not currently provided by jackosn-module-kotlin
.
This means that Cannot find a (Map) Key deserializer
should be thrown as for other classes.
It is still possible to deserialize a value class
as a Map
key by registering the appropriate key deserializer.
For these reasons, I interpret this as a feature request.
Maybe the difference here is wrt serialization (where "value.toString()" is used for "unknown" types) vs deserialization (where exception will be thrown)? Handling differs quite a bit b/w reading (deser) and writing (ser).
@cowtowncoder I have two questions about
Jackson
functionality.I don't believe there is a
JsonCreator
like feature used to deserialize keys, is this correct?
No I don't think so; except for @JsonKey
annotation... use of which might work via custom AnnotationIntrospector
.
Also, are there any caveats about searching for another
KeyDeserializer
from aKeyDeserializer
? https://github.com/ProjectMapK/jackson-module-kogera/pull/224/files#diff-b03a6f91081da3aa22c53b161b0a2f6f1a0cf4ba318329907046b5f5335b17c8R72-R73
Not sure; in general ideally would be resolved earlier and not during reading (in fact, it really should be done during construction/resolution for deserialization; sometimes not doable for serialization). But other than that I suspect it should be fine.
except for @JsonKey annotation... use of which might work via custom AnnotationIntrospector.
Is this not a function for @JsonValue
?
I wanted to make sure there is no such thing as @JsonKeyCreator
.
I will not implement this feature in kotlin-module
until the following issues are resolved, as it will override custom implementations defined by the user.
https://github.com/FasterXML/jackson-databind/issues/4444
I am unable to work on a fix as my busy state has become rather worse. If anyone has a strong desire for this feature, I would appreciate it if you could fix it.
Is this not a function for @JsonValue? I wanted to make sure there is no such thing as @JsonKeyCreator
No, as name implies, it is for JSON values -- keys are specifically separate thing (from Jackson perspective), key names for java.util.Map
s bound from JSON Object property names (vs property values). Part of the problem is that Property names are exposed as JsonToken.FIELD_NAME
so regular value JsonDeserializer
cannot be used; and thereby there is separate KeyDeserializer
.
At any rate, handling is quite separate and as such I don't see how @JsonValue
could be used outside limited cases (like with maybe Enum
type)
But there are also other relevant annotations: @JsonDeserialize(keyUsing = <KeyDeserializer class>
and @JsonSerialize(keyUsing = <key serializer class>
that can be used on class to mark handler(s).