Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When decodeFromJsonElement, enum parameters with no default value throw NoSuchElementException #2222

Closed
dahyun1226 opened this issue Mar 8, 2023 · 3 comments
Assignees

Comments

@dahyun1226
Copy link

Describe the bug
The decodeFromJsonElement function uses the JsonTreeDecoder when element is a JsonObject.
However, the decodeElementIndex function in the JsonTreeDecoder can reach the coerceInputValue function with the explicitNulls = false option and coerceInputValues = true option provided by Json, and the situation of enum parameters with no default value.

image

In coerceInputValue function, currentElement(tag) can throw NoSuchElementException because currentElement assume that value with tag exist.

image

image

I don't think this was intended.

To Reproduce
It's a bit of a contrived situation test, but I think it's good enough to reproduce the situation.
The following tests will fail.

    @Serializable
    enum class TestEnum {
        ENUM_A
    }

    @OptIn(ExperimentalSerializationApi::class)
    private val json = Json {
        prettyPrint = false
        ignoreUnknownKeys = true
        encodeDefaults = true
        explicitNulls = false
        coerceInputValues = true
    }

    object TestClassSerializer : KSerializer<TestClass> {
        override val descriptor: SerialDescriptor
            get() = TestClassSurrogate.serializer().descriptor

        override fun deserialize(decoder: Decoder): TestClass {
            val surrogate = decoder.decodeSerializableValue(TestClassSurrogate.serializer())
            return TestClass(testEnum = surrogate.testEnum)
        }

        override fun serialize(encoder: Encoder, value: TestClass) {}
    }

    @Serializable(with = TestClassSerializer::class)
    data class TestClass(val testEnum: TestEnum?)

    @Serializable
    data class TestClassSurrogate(val testEnum: TestEnum?)

    @Test
    fun test() {
        val jsonString = """{}""".trimIndent()

        val jsonElement = json.decodeFromString(JsonElement.serializer(), jsonString)

        val actualTestClass = json.decodeFromJsonElement<TestClass>(jsonElement)

        Truth.assertThat(actualTestClass.testEnum).isNull()
    }

Environment

  • Kotlin version: [1.7.10]
  • Library version: [1.4.1]
  • Kotlin platforms: [JVM]
  • Gradle version: [7.4]
  • IDE version (if bug is related to the IDE) [Android Studio Electric Eel 2022.1.1 Patch 2]
@sandwwraith
Copy link
Member

Please check 1.5.0 — it has fix for #2170 which looks similar

@dahyun1226
Copy link
Author

dahyun1226 commented Mar 15, 2023

The error still occurs in version 1.5.0.
It is still the same to use currentElement when accessing peekNull, and this is where the error occurs.
A simpler example:

    @Serializable
    enum class TestEnum {
        ENUM_A, DEFAULT
    }

    @Test
    fun `test`() {
        @Serializable
        data class TestClass(val testEnum: TestEnum?)

        val jsonString = """{}""".trimIndent()

        val jsonElement = Json {
            prettyPrint = false
            ignoreUnknownKeys = true
            encodeDefaults = true
            explicitNulls = false
            coerceInputValues = true
        }.decodeFromString<JsonElement>(jsonString)
        val actualTestClass = deserializeJsonElement<TestClass>(jsonElement)

        assertThat(actualTestClass.testEnum).isNull()
    }

@sandwwraith
Copy link
Member

I've checked on the current dev, and the issue is not present anymore. It was likely fixed by #2530. Also, linking #2586 as a reference — it may be related.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants