@@ -5,6 +5,11 @@ import com.fasterxml.jackson.core.exc.InputCoercionException
5
5
import com.fasterxml.jackson.databind.*
6
6
import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer
7
7
import 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
8
13
9
14
// The reason why key is treated as nullable is to match the tentative behavior of StdKeyDeserializer.
10
15
// If StdKeyDeserializer is modified, need to modify this too.
@@ -65,18 +70,68 @@ internal object ULongKeyDeserializer : StdKeyDeserializer(TYPE_LONG, ULong::clas
65
70
}
66
71
}
67
72
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 ]
70
79
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() {
71
121
override fun findKeyDeserializer (
72
122
type : JavaType ,
73
123
config : DeserializationConfig ? ,
74
124
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
+ }
81
136
}
82
137
}
0 commit comments