11package com.fasterxml.jackson.module.kotlin
22
3+ import com.fasterxml.jackson.annotation.JsonValue
34import com.fasterxml.jackson.core.JsonGenerator
45import com.fasterxml.jackson.databind.BeanDescription
56import com.fasterxml.jackson.databind.JavaType
@@ -8,8 +9,9 @@ import com.fasterxml.jackson.databind.SerializationConfig
89import com.fasterxml.jackson.databind.SerializerProvider
910import com.fasterxml.jackson.databind.ser.Serializers
1011import com.fasterxml.jackson.databind.ser.std.StdSerializer
12+ import java.lang.reflect.Method
13+ import java.lang.reflect.Modifier
1114import java.math.BigInteger
12- import kotlin.reflect.KClass
1315
1416object 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