From 4f3b782f07c0ca28113de282d54a8f9a3e8e6d98 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 6 May 2023 19:09:46 +0900 Subject: [PATCH 1/4] Implementation in Converter, replacing Serializer Related to #671, #674. --- .../fasterxml/jackson/module/kotlin/Converters.kt | 12 ++++++++++++ .../module/kotlin/KotlinAnnotationIntrospector.kt | 14 +++++++++++--- .../jackson/module/kotlin/KotlinSerializers.kt | 7 ------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/Converters.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/Converters.kt index 498b3312..71603251 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/Converters.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/Converters.kt @@ -1,9 +1,21 @@ package com.fasterxml.jackson.module.kotlin +import com.fasterxml.jackson.databind.JavaType import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer +import com.fasterxml.jackson.databind.type.TypeFactory import com.fasterxml.jackson.databind.util.StdConverter import kotlin.reflect.KClass +internal class SequenceToIteratorConverter(private val input: JavaType) : StdConverter, Iterator<*>>() { + override fun convert(value: Sequence<*>): Iterator<*> = value.iterator() + + override fun getInputType(typeFactory: TypeFactory): JavaType = input + // element-type may not be obtained, so a null check is required + override fun getOutputType(typeFactory: TypeFactory): JavaType = input.containedType(0) + ?.let { typeFactory.constructCollectionLikeType(Iterator::class.java, it) } + ?: typeFactory.constructType(Iterator::class.java) +} + // S is nullable because value corresponds to a nullable value class // @see KotlinNamesAnnotationIntrospector.findNullSerializer internal class ValueClassBoxConverter( diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt index 8296681e..b67e8e56 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt @@ -64,9 +64,17 @@ internal class KotlinAnnotationIntrospector(private val context: Module.SetupCon return super.findCreatorAnnotation(config, a) } - // Find a converter to handle the case where the getter returns an unboxed value from the value class. - override fun findSerializationConverter(a: Annotated): Converter<*, *>? = (a as? AnnotatedMethod)?.let { _ -> - cache.findValueClassReturnType(a)?.let { cache.getValueClassBoxConverter(a.rawReturnType, it) } + override fun findSerializationConverter(a: Annotated): Converter<*, *>? = when (a) { + // Find a converter to handle the case where the getter returns an unboxed value from the value class. + is AnnotatedMethod -> cache.findValueClassReturnType(a) + ?.let { cache.getValueClassBoxConverter(a.rawReturnType, it) } + ?: a.takeIf { Sequence::class.java.isAssignableFrom(it.rawType) } + ?.let { SequenceToIteratorConverter(it.type) } + + is AnnotatedClass -> a + .takeIf { Sequence::class.java.isAssignableFrom(it.rawType) } + ?.let { SequenceToIteratorConverter(it.type) } + else -> null } // Determine if the unbox result of value class is nullAable diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinSerializers.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinSerializers.kt index 4d785f7f..4b865fbc 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinSerializers.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinSerializers.kt @@ -13,12 +13,6 @@ import java.lang.reflect.Method import java.lang.reflect.Modifier import java.math.BigInteger -object SequenceSerializer : StdSerializer>(Sequence::class.java) { - override fun serialize(value: Sequence<*>, gen: JsonGenerator, provider: SerializerProvider) { - provider.defaultSerializeValue(value.iterator(), gen) - } -} - object UByteSerializer : StdSerializer(UByte::class.java) { override fun serialize(value: UByte, gen: JsonGenerator, provider: SerializerProvider) = gen.writeNumber(value.toShort()) @@ -98,7 +92,6 @@ internal class KotlinSerializers : Serializers.Base() { val rawClass = type.rawClass return when { - Sequence::class.java.isAssignableFrom(rawClass) -> SequenceSerializer UByte::class.java.isAssignableFrom(rawClass) -> UByteSerializer UShort::class.java.isAssignableFrom(rawClass) -> UShortSerializer UInt::class.java.isAssignableFrom(rawClass) -> UIntSerializer From 64213dc98bc4b689ff0a29e8c214d556b8e9f3a4 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 6 May 2023 19:18:55 +0900 Subject: [PATCH 2/4] Add test cases --- .../module/kotlin/test/SequenceSerdesTests.kt | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/SequenceSerdesTests.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/SequenceSerdesTests.kt index 20e49aad..2f9245c3 100644 --- a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/SequenceSerdesTests.kt +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/test/SequenceSerdesTests.kt @@ -1,5 +1,9 @@ package com.fasterxml.jackson.module.kotlin.test +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.StdSerializer import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue import org.junit.Test @@ -42,4 +46,39 @@ class TestSequenceDeserializer { val result = objectMapper.writeValueAsString(data) assertEquals("{\"value\":[]}", result) } -} \ No newline at end of file + + class ContentSer : StdSerializer(String::class.java) { + override fun serialize(value: String, gen: JsonGenerator, provider: SerializerProvider) { + provider.defaultSerializeValue("$value-ser", gen) + } + } + + data class ListWrapper( + @JsonSerialize(contentUsing = ContentSer::class) val value: List + ) + + data class SequenceWrapper( + @JsonSerialize(contentUsing = ContentSer::class) + val value: Sequence + ) + + @Test + fun contentUsingTest() { + val mapper = jacksonObjectMapper() + + val listResult = mapper.writeValueAsString(ListWrapper(listOf("foo"))) + val sequenceResult = mapper.writeValueAsString(SequenceWrapper(sequenceOf("foo"))) + + assertEquals("""{"value":["foo-ser"]}""", sequenceResult) + assertEquals(listResult, sequenceResult) + } + + // @see #674 + @Test + fun sequenceOfTest() { + val mapper = jacksonObjectMapper() + val result = mapper.writeValueAsString(sequenceOf("foo")) + + assertEquals("""["foo"]""", result) + } +} From b039c717f147334052af086c0766ca4d3d52aee9 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 6 May 2023 19:35:01 +0900 Subject: [PATCH 3/4] Deletion was cancelled and Deprecated was granted. Because it was public. --- .../jackson/module/kotlin/KotlinSerializers.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinSerializers.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinSerializers.kt index 4b865fbc..05e0dd2e 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinSerializers.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinSerializers.kt @@ -13,6 +13,16 @@ import java.lang.reflect.Method import java.lang.reflect.Modifier import java.math.BigInteger +@Deprecated( + message = "This class will be removed in 2.16 or later as it has been replaced by SequenceToIteratorConverter.", + replaceWith = ReplaceWith("com.fasterxml.jackson.module.kotlin.SequenceToIteratorConverter") +) +object SequenceSerializer : StdSerializer>(Sequence::class.java) { + override fun serialize(value: Sequence<*>, gen: JsonGenerator, provider: SerializerProvider) { + provider.defaultSerializeValue(value.iterator(), gen) + } +} + object UByteSerializer : StdSerializer(UByte::class.java) { override fun serialize(value: UByte, gen: JsonGenerator, provider: SerializerProvider) = gen.writeNumber(value.toShort()) From 632d4bbd015d5e7f3ace027264ddd3ec1893115b Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 6 May 2023 19:49:19 +0900 Subject: [PATCH 4/4] Remove unnecessary code introduced during development --- .../jackson/module/kotlin/KotlinAnnotationIntrospector.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt index b67e8e56..a9b53951 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/KotlinAnnotationIntrospector.kt @@ -68,9 +68,6 @@ internal class KotlinAnnotationIntrospector(private val context: Module.SetupCon // Find a converter to handle the case where the getter returns an unboxed value from the value class. is AnnotatedMethod -> cache.findValueClassReturnType(a) ?.let { cache.getValueClassBoxConverter(a.rawReturnType, it) } - ?: a.takeIf { Sequence::class.java.isAssignableFrom(it.rawType) } - ?.let { SequenceToIteratorConverter(it.type) } - is AnnotatedClass -> a .takeIf { Sequence::class.java.isAssignableFrom(it.rawType) } ?.let { SequenceToIteratorConverter(it.type) }