Skip to content

coerceInputValues prevent custom enum serializer to be used #1947

Open
@NoZomIBK

Description

@NoZomIBK

Describe the bug
Having Json configured with coerceInputValues = true results in Enums not being deserialized if they have a custom serializer. If a value is predefined in the data object, it uses the predefined value, if it has to be passed as a constructor parameter, deserialization fails because it is "missing"
Set a breakpoint in the first line of BarSerialiizer.deserialize, but debugger did not go there, no further error message etc.

To Reproduce

import kotlinx.serialization.*
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.SerialKind.ENUM
import kotlinx.serialization.descriptors.buildSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals

@Serializable
enum class Foo {
    A,
    B
}

@Serializable(with = BarSerializer::class)
enum class Bar {
    C,
    D
}

object BarSerializer : KSerializer<Bar> {
    override val descriptor: SerialDescriptor = buildSerialDescriptor("BarSerializer", ENUM)

    override fun deserialize(decoder: Decoder): Bar {
        val stringValue = decoder.decodeString()

        return try {
            Bar.valueOf(stringValue)
        }
        catch (e: IllegalArgumentException) {
            Bar.C
        }
    }

    override fun serialize(encoder: Encoder, value: Bar) {
        encoder.encodeString(value.name)
    }
}

@Serializable
class SomeClass {
    var foo: Foo = Foo.A
    var bar: Bar = Bar.C
}

@Serializable
class OtherClass(var foo:Foo,var bar:Bar)

class EnumBugTest {
    
    @Test
    fun serializerNotUsed() { 
        // given
        val json = """
            {
                "foo":"B",
                "bar":"D"
            }
        """.trimIndent()

        // when
        val obj: SomeClass = Json {
            coerceInputValues = true
        }.decodeFromString(json)

        // then
        assertEquals(Foo.B, obj.foo)
        assertEquals(Bar.D, obj.bar) // fails because it uses the default value Bar.C in SomeClass
    }
    
    @Test
    fun otherClassTest(){
        // given
        val json = """
            {
                "foo":"B",
                "bar":"D"
            }
        """.trimIndent()

        // when
        val obj: OtherClass = Json {
            coerceInputValues = true
        }.decodeFromString(json) // fails because field "bar" is "missing"

        // then
        assertEquals(Foo.B, obj.foo)
        assertEquals(Bar.D, obj.bar)
    }
}

Expected behavior
The defined serializer should be used

Environment

  • Kotlin version:1.6.21
  • Library version: 1.3.2
  • Kotlin platforms: JVM
  • Gradle version: 7.1.1

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions