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..a9b53951 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,14 @@ 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) } + 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..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,10 @@ 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) @@ -98,7 +102,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 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) + } +}