Skip to content

Commit a2e9042

Browse files
authored
Merge pull request #211 from ProjectMapK/fixes
Tweaks related to `value class` support in deserialization
2 parents 1490e6a + 9327444 commit a2e9042

File tree

9 files changed

+45
-37
lines changed

9 files changed

+45
-37
lines changed

src/main/java/io/github/projectmapk/jackson/module/kogera/deser/ValueClassDeserializer.java renamed to src/main/java/io/github/projectmapk/jackson/module/kogera/deser/WrapsNullableValueClassDeserializer.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,37 @@
11
package io.github.projectmapk.jackson.module.kogera.deser;
22

3+
import com.fasterxml.jackson.core.JacksonException;
4+
import com.fasterxml.jackson.core.JsonParser;
5+
import com.fasterxml.jackson.databind.DeserializationContext;
36
import com.fasterxml.jackson.databind.JavaType;
47
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
5-
import io.github.projectmapk.jackson.module.kogera.deser.deserializers.ValueClassBoxDeserializer;
8+
import io.github.projectmapk.jackson.module.kogera.deser.deserializers.WrapsNullableValueClassBoxDeserializer;
69
import kotlin.jvm.JvmClassMappingKt;
710
import kotlin.reflect.KClass;
811
import org.jetbrains.annotations.NotNull;
912
import org.jetbrains.annotations.Nullable;
1013

14+
import java.io.IOException;
15+
1116
/**
1217
* An interface to be inherited by JsonDeserializer that handles value classes that may wrap nullable.
13-
* @see ValueClassBoxDeserializer for implementation.
18+
* @see WrapsNullableValueClassBoxDeserializer for implementation.
1419
*/
15-
// To ensure maximum compatibility with StdDeserializer, this class is defined in Java.
16-
public abstract class ValueClassDeserializer<D> extends StdDeserializer<D> {
17-
protected ValueClassDeserializer(@NotNull KClass<?> vc) {
20+
// To ensure maximum compatibility with StdDeserializer, this class is written in Java.
21+
public abstract class WrapsNullableValueClassDeserializer<D> extends StdDeserializer<D> {
22+
protected WrapsNullableValueClassDeserializer(@NotNull KClass<?> vc) {
1823
super(JvmClassMappingKt.getJavaClass(vc));
1924
}
2025

21-
protected ValueClassDeserializer(@NotNull Class<?> vc) {
26+
protected WrapsNullableValueClassDeserializer(@NotNull Class<?> vc) {
2227
super(vc);
2328
}
2429

25-
protected ValueClassDeserializer(@NotNull JavaType valueType) {
30+
protected WrapsNullableValueClassDeserializer(@NotNull JavaType valueType) {
2631
super(valueType);
2732
}
2833

29-
protected ValueClassDeserializer(@NotNull StdDeserializer<D> src) {
34+
protected WrapsNullableValueClassDeserializer(@NotNull StdDeserializer<D> src) {
3035
super(src);
3136
}
3237

@@ -44,4 +49,8 @@ public final Class<D> handledType() {
4449
// It is defined so that null can also be returned so that Nulls.SKIP can be applied.
4550
@Nullable
4651
public abstract D getBoxedNullValue();
52+
53+
@Override
54+
public abstract D deserialize(@NotNull JsonParser p, @NotNull DeserializationContext ctxt)
55+
throws IOException, JacksonException;
4756
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ internal class ValueClassUnboxConverter<T : Any>(val valueClass: Class<T>) : Std
2929
private val unboxMethod = valueClass.getDeclaredMethod("unbox-impl").apply {
3030
if (!this.isAccessible) this.isAccessible = true
3131
}
32-
val unboxedClass: Class<*> = unboxMethod.returnType
3332

3433
override fun convert(value: T): Any? = unboxMethod.invoke(value)
3534

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import io.github.projectmapk.jackson.module.kogera.KotlinDuration
1414
import io.github.projectmapk.jackson.module.kogera.ReflectionCache
1515
import io.github.projectmapk.jackson.module.kogera.ValueClassBoxConverter
1616
import io.github.projectmapk.jackson.module.kogera.deser.JavaToKotlinDurationConverter
17-
import io.github.projectmapk.jackson.module.kogera.deser.ValueClassDeserializer
17+
import io.github.projectmapk.jackson.module.kogera.deser.WrapsNullableValueClassDeserializer
1818
import io.github.projectmapk.jackson.module.kogera.hasCreatorAnnotation
1919
import io.github.projectmapk.jackson.module.kogera.isUnboxableValueClass
2020
import io.github.projectmapk.jackson.module.kogera.toSignature
@@ -89,10 +89,10 @@ internal object ULongDeserializer : StdDeserializer<ULong>(ULong::class.java) {
8989
ULongChecker.readWithRangeCheck(p, p.bigIntegerValue)
9090
}
9191

92-
internal class ValueClassBoxDeserializer<S, D : Any>(
92+
internal class WrapsNullableValueClassBoxDeserializer<S, D : Any>(
9393
private val creator: Method,
9494
private val converter: ValueClassBoxConverter<S, D>
95-
) : ValueClassDeserializer<D>(converter.boxedClass) {
95+
) : WrapsNullableValueClassDeserializer<D>(converter.boxedClass) {
9696
private val inputType: Class<*> = creator.parameterTypes[0]
9797

9898
init {
@@ -169,9 +169,9 @@ internal class KotlinDeserializers(
169169
rawClass == KotlinDuration::class.java ->
170170
JavaToKotlinDurationConverter.takeIf { useJavaDurationConversion }?.delegatingDeserializer
171171
rawClass.isUnboxableValueClass() -> findValueCreator(type, rawClass, cache.getJmClass(rawClass)!!)?.let {
172-
val unboxedClass = cache.getValueClassUnboxConverter(rawClass).unboxedClass
172+
val unboxedClass = it.returnType
173173
val converter = cache.getValueClassBoxConverter(unboxedClass, rawClass)
174-
ValueClassBoxDeserializer(it, converter)
174+
WrapsNullableValueClassBoxDeserializer(it, converter)
175175
}
176176
else -> null
177177
}

src/main/kotlin/io/github/projectmapk/jackson/module/kogera/deser/valueInstantiator/KotlinValueInstantiator.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator
1414
import com.fasterxml.jackson.databind.exc.InvalidNullException
1515
import com.fasterxml.jackson.databind.module.SimpleValueInstantiators
1616
import io.github.projectmapk.jackson.module.kogera.ReflectionCache
17-
import io.github.projectmapk.jackson.module.kogera.deser.ValueClassDeserializer
17+
import io.github.projectmapk.jackson.module.kogera.deser.WrapsNullableValueClassDeserializer
1818
import io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.creator.ConstructorValueCreator
1919
import io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.creator.MethodValueCreator
2020
import io.github.projectmapk.jackson.module.kogera.deser.valueInstantiator.creator.ValueCreator
@@ -47,7 +47,7 @@ internal class KotlinValueInstantiator(
4747
isNullableParam: Boolean,
4848
valueDeserializer: JsonDeserializer<*>?
4949
): Boolean = !isNullableParam &&
50-
valueDeserializer is ValueClassDeserializer<*> &&
50+
valueDeserializer is WrapsNullableValueClassDeserializer<*> &&
5151
cache.getJmClass(valueDeserializer.handledType())!!.wrapsNullValueClass()
5252

5353
private val valueCreator: ValueCreator<*>? by ReflectProperties.lazySoft {
@@ -83,7 +83,7 @@ internal class KotlinValueInstantiator(
8383
// Deserializer.getNullValue could not be used because there is no way to get and parse parameters
8484
// from the BeanDescription and using AnnotationIntrospector would override user customization.
8585
if (requireValueClassSpecialNullValue(paramDef.isNullable, valueDeserializer)) {
86-
(valueDeserializer as ValueClassDeserializer<*>).boxedNullValue?.let { return@run it }
86+
(valueDeserializer as WrapsNullableValueClassDeserializer<*>).boxedNullValue?.let { return@run it }
8787
}
8888

8989
if (jsonProp.skipNulls() && paramDef.isOptional) return@forEachIndexed

src/test/kotlin/io/github/projectmapk/jackson/module/kogera/zIntegration/deser/valueClass/NullableObjectEdgeCases.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.DeserializationContext
77
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
88
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
99
import com.fasterxml.jackson.databind.exc.ValueInstantiationException
10-
import io.github.projectmapk.jackson.module.kogera.deser.ValueClassDeserializer
10+
import io.github.projectmapk.jackson.module.kogera.deser.WrapsNullableValueClassDeserializer
1111
import io.github.projectmapk.jackson.module.kogera.jacksonObjectMapper
1212
import io.github.projectmapk.jackson.module.kogera.readValue
1313
import org.junit.jupiter.api.Assertions.assertEquals
@@ -42,7 +42,7 @@ class NullableObjectEdgeCases {
4242
assertEquals(NullValue(NullValueDeserializer.nv, NullValueDeserializer.nv), result)
4343
}
4444

45-
class NullsSkipDeserializer : ValueClassDeserializer<VC>(VC::class) {
45+
class NullsSkipDeserializerWrapsNullable : WrapsNullableValueClassDeserializer<VC>(VC::class) {
4646
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): VC {
4747
TODO("Not yet implemented")
4848
}
@@ -52,7 +52,7 @@ class NullableObjectEdgeCases {
5252

5353
data class NullsSkip(
5454
@field:JsonSetter(nulls = Nulls.SKIP)
55-
@field:JsonDeserialize(using = NullsSkipDeserializer::class)
55+
@field:JsonDeserialize(using = NullsSkipDeserializerWrapsNullable::class)
5656
val nn: VC = VC("skip"),
5757
@field:JsonSetter(nulls = Nulls.SKIP)
5858
val n: VC? = VC("skip")

src/test/kotlin/io/github/projectmapk/jackson/module/kogera/zIntegration/deser/valueClass/ValueClasses.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package io.github.projectmapk.jackson.module.kogera.zIntegration.deser.valueClas
33
import com.fasterxml.jackson.core.JsonParser
44
import com.fasterxml.jackson.databind.DeserializationContext
55
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
6-
import io.github.projectmapk.jackson.module.kogera.deser.ValueClassDeserializer
6+
import io.github.projectmapk.jackson.module.kogera.deser.WrapsNullableValueClassDeserializer
77

88
@JvmInline
99
value class Primitive(val v: Int) {
@@ -22,7 +22,7 @@ value class NonNullObject(val v: String) {
2222

2323
@JvmInline
2424
value class NullableObject(val v: String?) {
25-
class Deserializer : ValueClassDeserializer<NullableObject>(NullableObject::class) {
25+
class DeserializerWrapsNullable : WrapsNullableValueClassDeserializer<NullableObject>(NullableObject::class) {
2626
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): NullableObject =
2727
NullableObject(p.valueAsString + "-deser")
2828

src/test/kotlin/io/github/projectmapk/jackson/module/kogera/zIntegration/deser/valueClass/deserializer/SpecifiedForObjectMapperTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class SpecifiedForObjectMapperTest {
1717
val module = SimpleModule().apply {
1818
this.addDeserializer(Primitive::class.java, Primitive.Deserializer())
1919
this.addDeserializer(NonNullObject::class.java, NonNullObject.Deserializer())
20-
this.addDeserializer(NullableObject::class.java, NullableObject.Deserializer())
20+
this.addDeserializer(NullableObject::class.java, NullableObject.DeserializerWrapsNullable())
2121
}
2222
this.registerModule(module)
2323
}

src/test/kotlin/io/github/projectmapk/jackson/module/kogera/zIntegration/deser/valueClass/deserializer/byAnnotation/specifiedForProperty/NullableObjectTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ class NullableObjectTest {
1414
}
1515

1616
data class NonNull(
17-
@get:JsonDeserialize(using = NullableObject.Deserializer::class)
17+
@get:JsonDeserialize(using = NullableObject.DeserializerWrapsNullable::class)
1818
val getterAnn: NullableObject,
19-
@field:JsonDeserialize(using = NullableObject.Deserializer::class)
19+
@field:JsonDeserialize(using = NullableObject.DeserializerWrapsNullable::class)
2020
val fieldAnn: NullableObject
2121
)
2222

@@ -34,9 +34,9 @@ class NullableObjectTest {
3434
}
3535

3636
data class Nullable(
37-
@get:JsonDeserialize(using = NullableObject.Deserializer::class)
37+
@get:JsonDeserialize(using = NullableObject.DeserializerWrapsNullable::class)
3838
val getterAnn: NullableObject?,
39-
@field:JsonDeserialize(using = NullableObject.Deserializer::class)
39+
@field:JsonDeserialize(using = NullableObject.DeserializerWrapsNullable::class)
4040
val fieldAnn: NullableObject?
4141
)
4242

src/test/kotlin/io/github/projectmapk/jackson/module/kogera/zIntegration/deser/valueClass/jsonCreator/HandledByJacksonTest.kt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,38 @@ import org.junit.jupiter.api.Test
99
// Test for Creator that can be handled by the Jackson mechanism.
1010
class HandledByJacksonTest {
1111
@JvmInline
12-
value class PrimitiveNullableCreator(val value: Int) {
12+
value class PrimitiveMultiParamCreator(val value: Int) {
1313
companion object {
1414
// Avoiding unboxing by making the return value of Creator nullable
1515
@JvmStatic
1616
@JsonCreator
17-
fun creator(first: Int, second: Int): PrimitiveNullableCreator? =
18-
PrimitiveNullableCreator(first + second)
17+
fun creator(first: Int, second: Int): PrimitiveMultiParamCreator? =
18+
PrimitiveMultiParamCreator(first + second)
1919
}
2020
}
2121

2222
@Test
2323
fun primitiveNullableCreatorTest() {
2424
val mapper = jacksonObjectMapper()
25-
val r: PrimitiveNullableCreator = mapper.readValue("""{"first":1,"second":2}""")
26-
assertEquals(PrimitiveNullableCreator(3), r)
25+
val r: PrimitiveMultiParamCreator = mapper.readValue("""{"first":1,"second":2}""")
26+
assertEquals(PrimitiveMultiParamCreator(3), r)
2727
}
2828

2929
@JvmInline
30-
value class NullableObjectNullableCreator(val value: Int?) {
30+
value class NullableObjectMiltiParamCreator(val value: Int?) {
3131
companion object {
3232
// Avoiding unboxing by making the return value of Creator nullable
3333
@JvmStatic
3434
@JsonCreator
35-
fun creator(first: Int, second: Int): NullableObjectNullableCreator? =
36-
NullableObjectNullableCreator(first + second)
35+
fun creator(first: Int, second: Int): NullableObjectMiltiParamCreator? =
36+
NullableObjectMiltiParamCreator(first + second)
3737
}
3838
}
3939

4040
@Test
4141
fun nullableObjectNullableCreatorTest() {
4242
val mapper = jacksonObjectMapper()
43-
val r: NullableObjectNullableCreator = mapper.readValue("""{"first":1,"second":2}""")
44-
assertEquals(NullableObjectNullableCreator(3), r)
43+
val r: NullableObjectMiltiParamCreator = mapper.readValue("""{"first":1,"second":2}""")
44+
assertEquals(NullableObjectMiltiParamCreator(3), r)
4545
}
4646
}

0 commit comments

Comments
 (0)