Skip to content

Commit 1c112c9

Browse files
authored
Merge pull request #531 from k163377/github-530/fix
Fix #530
2 parents c49144b + cc6c536 commit 1c112c9

File tree

4 files changed

+325
-20
lines changed

4 files changed

+325
-20
lines changed

pom.xml

+7
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,13 @@
208208
TODO Remove after release of 2.14 and update the oldVersion above to 2.14.
209209
-->
210210
<exclude>com.fasterxml.jackson.module.kotlin.ValueClassBoxSerializer</exclude>
211+
<exclude>com.fasterxml.jackson.module.kotlin.ValueClassSerializer$StaticJsonValue</exclude>
212+
<exclude>com.fasterxml.jackson.module.kotlin.ValueClassSerializer$Unbox</exclude>
213+
<exclude>com.fasterxml.jackson.module.kotlin.ValueClassStaticJsonValueSerializer</exclude>
214+
<exclude>com.fasterxml.jackson.module.kotlin.ValueClassUnboxSerializer.serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)</exclude>
215+
<exclude>com.fasterxml.jackson.databind.jsonschema.SchemaAware[com.fasterxml.jackson.databind.jsonschema.SchemaAware]</exclude>
216+
<exclude>com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable[com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable]:INTERFACE_REMOVED, java.io.Serializable[java.io.Serializable]</exclude>
217+
<exclude>com.fasterxml.jackson.module.kotlin.ValueClassUnboxSerializer</exclude>
211218
</excludes>
212219
</parameter>
213220
</configuration>

src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt

+6-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.Module
77
import com.fasterxml.jackson.databind.cfg.MapperConfig
88
import com.fasterxml.jackson.databind.introspect.*
99
import com.fasterxml.jackson.databind.jsontype.NamedType
10+
import com.fasterxml.jackson.databind.ser.std.StdSerializer
1011
import java.lang.reflect.AccessibleObject
1112
import java.lang.reflect.Constructor
1213
import java.lang.reflect.Field
@@ -62,7 +63,7 @@ internal class KotlinAnnotationIntrospector(private val context: Module.SetupCon
6263
}
6364

6465
// Find a serializer to handle the case where the getter returns an unboxed value from the value class.
65-
override fun findSerializer(am: Annotated): ValueClassBoxSerializer<*>? = when (am) {
66+
override fun findSerializer(am: Annotated): StdSerializer<*>? = when (am) {
6667
is AnnotatedMethod -> {
6768
val getter = am.member.apply {
6869
// If the return value of the getter is a value class,
@@ -87,8 +88,10 @@ internal class KotlinAnnotationIntrospector(private val context: Module.SetupCon
8788
?.takeIf { it.isValue }
8889
?.java
8990
?.let { outerClazz ->
90-
@Suppress("UNCHECKED_CAST")
91-
ValueClassBoxSerializer(outerClazz, getter.returnType)
91+
val innerClazz = getter.returnType
92+
93+
ValueClassStaticJsonValueSerializer.createdOrNull(outerClazz, innerClazz)
94+
?: @Suppress("UNCHECKED_CAST") ValueClassBoxSerializer(outerClazz, innerClazz)
9295
}
9396
}
9497
// Ignore the case of AnnotatedField, because JvmField cannot be set in the field of value class.

src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinSerializers.kt

+78-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.fasterxml.jackson.module.kotlin
22

3+
import com.fasterxml.jackson.annotation.JsonValue
34
import com.fasterxml.jackson.core.JsonGenerator
45
import com.fasterxml.jackson.databind.BeanDescription
56
import com.fasterxml.jackson.databind.JavaType
@@ -8,8 +9,9 @@ import com.fasterxml.jackson.databind.SerializationConfig
89
import com.fasterxml.jackson.databind.SerializerProvider
910
import com.fasterxml.jackson.databind.ser.Serializers
1011
import com.fasterxml.jackson.databind.ser.std.StdSerializer
12+
import java.lang.reflect.Method
13+
import java.lang.reflect.Modifier
1114
import java.math.BigInteger
12-
import kotlin.reflect.KClass
1315

1416
object SequenceSerializer : StdSerializer<Sequence<*>>(Sequence::class.java) {
1517
override fun serialize(value: Sequence<*>, gen: JsonGenerator, provider: SerializerProvider) {
@@ -43,16 +45,47 @@ object ULongSerializer : StdSerializer<ULong>(ULong::class.java) {
4345
}
4446
}
4547

46-
object ValueClassUnboxSerializer : StdSerializer<Any>(Any::class.java) {
47-
override fun serialize(value: Any, gen: JsonGenerator, provider: SerializerProvider) {
48-
val unboxed = value::class.java.getMethod("unbox-impl").invoke(value)
48+
// Class must be UnboxableValueClass.
49+
private fun Class<*>.getStaticJsonValueGetter(): Method? = this.declaredMethods
50+
.find { method -> Modifier.isStatic(method.modifiers) && method.annotations.any { it is JsonValue } }
4951

50-
if (unboxed == null) {
51-
provider.findNullValueSerializer(null).serialize(unboxed, gen, provider)
52-
return
52+
internal sealed class ValueClassSerializer<T : Any>(t: Class<T>) : StdSerializer<T>(t) {
53+
object Unbox : ValueClassSerializer<Any>(Any::class.java) {
54+
override fun serialize(value: Any, gen: JsonGenerator, provider: SerializerProvider) {
55+
val unboxed = value::class.java.getMethod("unbox-impl").invoke(value)
56+
57+
if (unboxed == null) {
58+
provider.findNullValueSerializer(null).serialize(unboxed, gen, provider)
59+
return
60+
}
61+
62+
provider.findValueSerializer(unboxed::class.java).serialize(unboxed, gen, provider)
63+
}
64+
}
65+
66+
class StaticJsonValue<T : Any>(
67+
t: Class<T>, private val staticJsonValueGetter: Method
68+
) : ValueClassSerializer<T>(t) {
69+
private val unboxMethod: Method = t.getMethod("unbox-impl")
70+
71+
override fun serialize(value: T, gen: JsonGenerator, provider: SerializerProvider) {
72+
val unboxed = unboxMethod.invoke(value)
73+
// As shown in the processing of the factory function, jsonValueGetter is always a static method.
74+
val jsonValue: Any? = staticJsonValueGetter.invoke(null, unboxed)
75+
jsonValue
76+
?.let { provider.findValueSerializer(it::class.java).serialize(it, gen, provider) }
77+
?: provider.findNullValueSerializer(null).serialize(null, gen, provider)
5378
}
79+
}
5480

55-
provider.findValueSerializer(unboxed::class.java).serialize(unboxed, gen, provider)
81+
companion object {
82+
// `t` must be UnboxableValueClass.
83+
// If create a function with a JsonValue in the value class,
84+
// it will be compiled as a static method (= cannot be processed properly by Jackson),
85+
// so use a ValueClassSerializer.StaticJsonValue to handle this.
86+
fun from(t: Class<*>): ValueClassSerializer<*> = t.getStaticJsonValueGetter()
87+
?.let { StaticJsonValue(t, it) }
88+
?: Unbox
5689
}
5790
}
5891

@@ -61,15 +94,19 @@ internal class KotlinSerializers : Serializers.Base() {
6194
config: SerializationConfig?,
6295
type: JavaType,
6396
beanDesc: BeanDescription?
64-
): JsonSerializer<*>? = when {
65-
Sequence::class.java.isAssignableFrom(type.rawClass) -> SequenceSerializer
66-
UByte::class.java.isAssignableFrom(type.rawClass) -> UByteSerializer
67-
UShort::class.java.isAssignableFrom(type.rawClass) -> UShortSerializer
68-
UInt::class.java.isAssignableFrom(type.rawClass) -> UIntSerializer
69-
ULong::class.java.isAssignableFrom(type.rawClass) -> ULongSerializer
70-
// The priority of Unboxing needs to be lowered so as not to break the serialization of Unsigned Integers.
71-
type.rawClass.isUnboxableValueClass() -> ValueClassUnboxSerializer
72-
else -> null
97+
): JsonSerializer<*>? {
98+
val rawClass = type.rawClass
99+
100+
return when {
101+
Sequence::class.java.isAssignableFrom(rawClass) -> SequenceSerializer
102+
UByte::class.java.isAssignableFrom(rawClass) -> UByteSerializer
103+
UShort::class.java.isAssignableFrom(rawClass) -> UShortSerializer
104+
UInt::class.java.isAssignableFrom(rawClass) -> UIntSerializer
105+
ULong::class.java.isAssignableFrom(rawClass) -> ULongSerializer
106+
// The priority of Unboxing needs to be lowered so as not to break the serialization of Unsigned Integers.
107+
rawClass.isUnboxableValueClass() -> ValueClassSerializer.from(rawClass)
108+
else -> null
109+
}
73110
}
74111
}
75112

@@ -89,3 +126,27 @@ internal class ValueClassBoxSerializer<T : Any>(
89126
provider.findValueSerializer(outerClazz).serialize(boxed, gen, provider)
90127
}
91128
}
129+
130+
internal class ValueClassStaticJsonValueSerializer<T> private constructor(
131+
innerClazz: Class<T>,
132+
private val staticJsonValueGetter: Method
133+
) : StdSerializer<T>(innerClazz) {
134+
override fun serialize(value: T?, gen: JsonGenerator, provider: SerializerProvider) {
135+
// As shown in the processing of the factory function, jsonValueGetter is always a static method.
136+
val jsonValue: Any? = staticJsonValueGetter.invoke(null, value)
137+
jsonValue
138+
?.let { provider.findValueSerializer(it::class.java).serialize(it, gen, provider) }
139+
?: provider.findNullValueSerializer(null).serialize(null, gen, provider)
140+
}
141+
142+
// Since JsonValue can be processed correctly if it is given to a non-static getter/field,
143+
// this class will only process if it is a `static` method.
144+
companion object {
145+
fun <T> createdOrNull(
146+
outerClazz: Class<out Any>,
147+
innerClazz: Class<T>
148+
): ValueClassStaticJsonValueSerializer<T>? = outerClazz
149+
.getStaticJsonValueGetter()
150+
?.let { ValueClassStaticJsonValueSerializer(innerClazz, it) }
151+
}
152+
}

0 commit comments

Comments
 (0)