@@ -5,6 +5,11 @@ import com.fasterxml.jackson.core.exc.InputCoercionException
55import com.fasterxml.jackson.databind.*
66import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer
77import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializers
8+ import com.fasterxml.jackson.databind.exc.InvalidDefinitionException
9+ import java.lang.reflect.Method
10+ import kotlin.reflect.KClass
11+ import kotlin.reflect.full.primaryConstructor
12+ import kotlin.reflect.jvm.javaMethod
813
914// The reason why key is treated as nullable is to match the tentative behavior of StdKeyDeserializer.
1015// If StdKeyDeserializer is modified, need to modify this too.
@@ -65,18 +70,68 @@ internal object ULongKeyDeserializer : StdKeyDeserializer(TYPE_LONG, ULong::clas
6570 }
6671}
6772
68- internal object KotlinKeyDeserializers : StdKeyDeserializers() {
69- private fun readResolve (): Any = KotlinKeyDeserializers
73+ // The implementation is designed to be compatible with various creators, just in case.
74+ internal class ValueClassKeyDeserializer <S , D : Any >(
75+ private val creator : Method ,
76+ private val converter : ValueClassBoxConverter <S , D >
77+ ) : KeyDeserializer() {
78+ private val unboxedClass: Class <* > = creator.parameterTypes[0 ]
7079
80+ init {
81+ creator.apply { if (! this .isAccessible) this .isAccessible = true }
82+ }
83+
84+ // Based on databind error
85+ // https://github.com/FasterXML/jackson-databind/blob/341f8d360a5f10b5e609d6ee0ea023bf597ce98a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java#L624
86+ private fun errorMessage (boxedType : JavaType ): String =
87+ " Could not find (Map) Key deserializer for types wrapped in $boxedType "
88+
89+ override fun deserializeKey (key : String? , ctxt : DeserializationContext ): Any {
90+ val unboxedJavaType = ctxt.constructType(unboxedClass)
91+
92+ return try {
93+ // findKeyDeserializer does not return null, and an exception will be thrown if not found.
94+ val value = ctxt.findKeyDeserializer(unboxedJavaType, null ).deserializeKey(key, ctxt)
95+ @Suppress(" UNCHECKED_CAST" )
96+ converter.convert(creator.invoke(null , value) as S )
97+ } catch (e: InvalidDefinitionException ) {
98+ throw JsonMappingException .from(ctxt, errorMessage(ctxt.constructType(converter.boxedClass.java)), e)
99+ }
100+ }
101+
102+ companion object {
103+ fun createOrNull (
104+ boxedClass : KClass <* >,
105+ cache : ReflectionCache
106+ ): ValueClassKeyDeserializer <* , * >? {
107+ // primaryConstructor.javaMethod for the value class returns constructor-impl
108+ // Only primary constructor is allowed as creator, regardless of visibility.
109+ // This is because it is based on the WrapsNullableValueClassBoxDeserializer.
110+ // Also, as far as I could research, there was no such functionality as JsonKeyCreator,
111+ // so it was not taken into account.
112+ val creator = boxedClass.primaryConstructor?.javaMethod ? : return null
113+ val converter = cache.getValueClassBoxConverter(creator.returnType, boxedClass)
114+
115+ return ValueClassKeyDeserializer (creator, converter)
116+ }
117+ }
118+ }
119+
120+ internal class KotlinKeyDeserializers (private val cache : ReflectionCache ) : StdKeyDeserializers() {
71121 override fun findKeyDeserializer (
72122 type : JavaType ,
73123 config : DeserializationConfig ? ,
74124 beanDesc : BeanDescription ?
75- ): KeyDeserializer ? = when (type.rawClass) {
76- UByte ::class .java -> UByteKeyDeserializer
77- UShort ::class .java -> UShortKeyDeserializer
78- UInt ::class .java -> UIntKeyDeserializer
79- ULong ::class .java -> ULongKeyDeserializer
80- else -> null
125+ ): KeyDeserializer ? {
126+ val rawClass = type.rawClass
127+
128+ return when {
129+ rawClass == UByte ::class .java -> UByteKeyDeserializer
130+ rawClass == UShort ::class .java -> UShortKeyDeserializer
131+ rawClass == UInt ::class .java -> UIntKeyDeserializer
132+ rawClass == ULong ::class .java -> ULongKeyDeserializer
133+ rawClass.isUnboxableValueClass() -> ValueClassKeyDeserializer .createOrNull(rawClass.kotlin, cache)
134+ else -> null
135+ }
81136 }
82137}
0 commit comments