Kotlin / kotlinx.serialization

Kotlin multiplatform / multi-format serialization
Apache License 2.0
5.41k stars 620 forks source link

'coerceInputValues' configuration option does not work for lists of Enums #1113

Open PhilipSA opened 4 years ago

PhilipSA commented 4 years ago

Describe the bug If you have an EnumClass and the json contains an element not present in the enum it will crash: kotlinx.serialization.SerializationException: com.example.testingstuff.Numbers does not contain element with name 'Two'

But since i have declared ignoreUnknownKeys = true i expect it to be either null or default to something.

I did not find anything about this in the documentation.

Also is there any workaround for this?

To Reproduce

enum class Numbers {
    One
}

@Serializable
data class EnumClass(
    val enumType: Numbers? = null
)

class ExampleUnitTest {
    @Test
    fun ignoreUnknownEnum() {
        val json = Json {
            ignoreUnknownKeys = true
        }
        //This will work
        var jsonString = "{\"enumType\": \"One\" }"
        json.decodeFromString(EnumClass.serializer(), jsonString)

        //This will crash
        jsonString = "{\"enumType\": \"Two\" }"
        json.decodeFromString(EnumClass.serializer(), jsonString)
    }
}

Expected behavior enumType becomes null or Numbers.One

Environment

qwwdfsad commented 4 years ago

It works as expected, ignoreUnknownKeys is designed to ignore unknown keys, not values. But you can use coerceInputValues = true for the very purpose you described

PhilipSA commented 4 years ago

It works as expected, ignoreUnknownKeys is designed to ignore unknown keys, not values. But you can use coerceInputValues = true for the very purpose you described

Nice that worked for the example case. But what about if it is a list of enums?

It still crashes when i change my example to this:

enum class Numbers {
    One
}

@Serializable
data class EnumClass(
    val enumType: List<Numbers?>
)

class ExampleUnitTest {
    @Test
    fun ignoreUnknownEnum() {
        val json = Json {
            ignoreUnknownKeys = true
            coerceInputValues = true
        }

        var jsonString = "{\"enumType\": [\"One\"] }"
        json.decodeFromString(EnumClass.serializer(), jsonString)

        jsonString = "{\"enumType\": [\"Two\"] }"
        json.decodeFromString(EnumClass.serializer(), jsonString)
    }
}
qwwdfsad commented 4 years ago

Currently, the only workaround is to write your own serializer. We may consider implementing this functionality as part of coerceInputValues

PhilipSA commented 4 years ago

Currently, the only workaround is to write your own serializer. We may consider implementing this functionality as part of coerceInputValues

Yes please. Consider this a feature request

sandwwraith commented 3 years ago

See also #1205

carstenhag commented 1 year ago

I don't really have more than a 👍 to contribute, but could this perhaps be looked at in the future? We would like to either mark one enum value as @Default using List, or receive null values with List<Enum?>, both would work.

DrMetallius commented 1 year ago

Is there any news regarding the implementation of this feature? It would be very useful to have the coerceInputValues support for lists of enums.

huthe98 commented 9 months ago

.

valeriyo commented 3 months ago

This is years old. What's the latest greatest workaround? if I would like all non-matching enum values to be mapped to a specified enum value (e.g. "Unknown") - I had a solution that worked in JVM (using reflection) but in a multiplatform project, it is no longer viable. Thanks.

pdvrieze commented 2 months ago

The best workaround is to use a custom serializer that just reads/writes the value as string and maps that to the needed enum values. Writing custom serializers is actually really easy, especially that only read/write primitives.