Skip to content

Commit fd559e7

Browse files
authored
Merge pull request #134 from ProjectMapK/value-class-deser-init-perf
Use ValueClassBoxConverter for deserialization box processing.
2 parents 3a513f9 + 3a5331e commit fd559e7

File tree

4 files changed

+36
-28
lines changed

4 files changed

+36
-28
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.github.projectmapk.jackson.module.kogera
2+
3+
import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer
4+
import com.fasterxml.jackson.databind.util.StdConverter
5+
6+
/**
7+
* A converter that only performs box processing for the value class.
8+
* Note that constructor-impl is not called.
9+
* @param S is nullable because value corresponds to a nullable value class.
10+
* see [io.github.projectmapk.jackson.module.kogera.annotation_introspector.KotlinFallbackAnnotationIntrospector.findNullSerializer]
11+
*/
12+
internal class ValueClassBoxConverter<S : Any?, D : Any>(
13+
unboxedClass: Class<S>,
14+
val valueClass: Class<D>
15+
) : StdConverter<S, D>() {
16+
private val boxMethod = valueClass.getDeclaredMethod("box-impl", unboxedClass).apply {
17+
if (!this.isAccessible) this.isAccessible = true
18+
}
19+
20+
@Suppress("UNCHECKED_CAST")
21+
override fun convert(value: S): D = boxMethod.invoke(null, value) as D
22+
23+
val delegatingSerializer: StdDelegatingSerializer by lazy { StdDelegatingSerializer(this) }
24+
}

src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ReflectionCache.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package io.github.projectmapk.jackson.module.kogera
22

33
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod
44
import com.fasterxml.jackson.databind.util.LRUMap
5-
import io.github.projectmapk.jackson.module.kogera.ser.ValueClassBoxConverter
65
import java.io.Serializable
76
import java.util.Optional
87

src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/deserializers/KotlinDeserializers.kt

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.fasterxml.jackson.databind.exc.InvalidDefinitionException
1212
import io.github.projectmapk.jackson.module.kogera.JmClass
1313
import io.github.projectmapk.jackson.module.kogera.KotlinDuration
1414
import io.github.projectmapk.jackson.module.kogera.ReflectionCache
15+
import io.github.projectmapk.jackson.module.kogera.ValueClassBoxConverter
1516
import io.github.projectmapk.jackson.module.kogera.deser.JavaToKotlinDurationConverter
1617
import io.github.projectmapk.jackson.module.kogera.hasCreatorAnnotation
1718
import io.github.projectmapk.jackson.module.kogera.isUnboxableValueClass
@@ -75,26 +76,23 @@ internal object ULongDeserializer : StdDeserializer<ULong>(ULong::class.java) {
7576
ULongChecker.readWithRangeCheck(p, p.bigIntegerValue)
7677
}
7778

78-
internal class ValueClassBoxDeserializer<T : Any>(
79+
internal class ValueClassBoxDeserializer<S, D : Any>(
7980
private val creator: Method,
80-
clazz: Class<T>
81-
) : StdDeserializer<T>(clazz) {
81+
private val converter: ValueClassBoxConverter<S, D>
82+
) : StdDeserializer<D>(converter.valueClass) {
8283
private val inputType: Class<*> = creator.parameterTypes[0]
83-
private val boxMethod: Method = clazz
84-
.getDeclaredMethod("box-impl", clazz.getDeclaredMethod("unbox-impl").returnType)
85-
.apply { if (!this.isAccessible) this.isAccessible = true }
8684

8785
init {
8886
creator.apply { if (!this.isAccessible) this.isAccessible = true }
8987
}
9088

91-
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): T {
89+
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): D {
9290
val input = p.readValueAs(inputType)
9391

9492
// To instantiate the value class in the same way as other classes,
9593
// it is necessary to call creator(e.g. constructor-impl) -> box-impl in that order.
9694
@Suppress("UNCHECKED_CAST")
97-
return boxMethod.invoke(null, creator.invoke(null, input)) as T
95+
return converter.convert(creator.invoke(null, input) as S)
9896
}
9997
}
10098

@@ -149,8 +147,11 @@ internal class KotlinDeserializers(
149147
rawClass == ULong::class.java -> ULongDeserializer
150148
rawClass == KotlinDuration::class.java ->
151149
JavaToKotlinDurationConverter.takeIf { useJavaDurationConversion }?.delegatingDeserializer
152-
rawClass.isUnboxableValueClass() -> findValueCreator(type, rawClass, cache.getJmClass(rawClass)!!)
153-
?.let { ValueClassBoxDeserializer(it, rawClass) }
150+
rawClass.isUnboxableValueClass() -> findValueCreator(type, rawClass, cache.getJmClass(rawClass)!!)?.let {
151+
val unboxedClass = rawClass.getMethod("unbox-impl").returnType
152+
val converter = cache.getValueClassBoxConverter(unboxedClass, rawClass)
153+
ValueClassBoxDeserializer(it, converter)
154+
}
154155
else -> null
155156
}
156157
}

src/main/kotlin/io/github/projectmapk/jackson/module/kogera/ser/Converters.kt

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package io.github.projectmapk.jackson.module.kogera.ser
22

33
import com.fasterxml.jackson.databind.JavaType
4-
import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer
54
import com.fasterxml.jackson.databind.type.TypeFactory
65
import com.fasterxml.jackson.databind.util.StdConverter
76
import io.github.projectmapk.jackson.module.kogera.JavaDuration
87
import io.github.projectmapk.jackson.module.kogera.KotlinDuration
8+
import io.github.projectmapk.jackson.module.kogera.ValueClassBoxConverter
99
import kotlin.time.toJavaDuration
1010

1111
internal class SequenceToIteratorConverter(private val input: JavaType) : StdConverter<Sequence<*>, Iterator<*>>() {
@@ -19,22 +19,6 @@ internal class SequenceToIteratorConverter(private val input: JavaType) : StdCon
1919
?: typeFactory.constructType(Iterator::class.java)
2020
}
2121

22-
// S is nullable because value corresponds to a nullable value class
23-
// @see KotlinFallbackAnnotationIntrospector.findNullSerializer
24-
internal class ValueClassBoxConverter<S : Any?, D : Any>(
25-
unboxedClass: Class<S>,
26-
valueClass: Class<D>
27-
) : StdConverter<S, D>() {
28-
private val boxMethod = valueClass.getDeclaredMethod("box-impl", unboxedClass).apply {
29-
if (!this.isAccessible) this.isAccessible = true
30-
}
31-
32-
@Suppress("UNCHECKED_CAST")
33-
override fun convert(value: S): D = boxMethod.invoke(null, value) as D
34-
35-
val delegatingSerializer: StdDelegatingSerializer by lazy { StdDelegatingSerializer(this) }
36-
}
37-
3822
internal object KotlinDurationValueToJavaDurationConverter : StdConverter<Long, JavaDuration>() {
3923
private val boxConverter by lazy { ValueClassBoxConverter(Long::class.java, KotlinDuration::class.java) }
4024

0 commit comments

Comments
 (0)