Skip to content

SerialName duplication for different polymorphic heirarchies causes collisons when namingStrategy is set. #3003

Open
@FlightOfStairs

Description

@FlightOfStairs

Describe the bug

We have parallel heirarchies of polymorphic classes. We were seeing fields in one heirarchy appearing in the serialized output of the other; only when namingStrategy is set.

To Reproduce

Kotest cases:

@file:OptIn(ExperimentalSerializationApi::class)

import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonClassDiscriminator
import kotlinx.serialization.json.JsonNamingStrategy

class SerialNameCollisionTest : FunSpec({
    test("Naming strategy set - test will fail") {
        val json = Json {
            namingStrategy = JsonNamingStrategy.SnakeCase
        }

        val foo = FooBoolean(true)
        val bar = BarBoolean(true)

        json.encodeToString<Foo>(foo) shouldBe """{"type":"BOOLEAN","my_foo_value":true}"""

        // Fails at this line:
        json.encodeToString<Bar>(bar) shouldBe """{"type":"BOOLEAN","my_bar_value":true}"""
        // Expected :"{"type":"BOOLEAN","my_bar_value":true}"
        // Actual   :"{"type":"BOOLEAN","my_foo_value":true}"
    }

    test("Naming strategy not set - test will pass") {
        val json = Json { }

        val foo = FooBoolean(true)
        val bar = BarBoolean(true)

        json.encodeToString<Foo>(foo) shouldBe """{"type":"BOOLEAN","myFooValue":true}"""
        json.encodeToString<Bar>(bar) shouldBe """{"type":"BOOLEAN","myBarValue":true}"""
    }
})

@Serializable
@JsonClassDiscriminator("type")
sealed interface Foo

@Serializable
@SerialName("BOOLEAN")
data class FooBoolean(val myFooValue: Boolean) : Foo

@Serializable
@JsonClassDiscriminator("type")
sealed interface Bar

@Serializable
@SerialName("BOOLEAN")
data class BarBoolean(val myBarValue: Boolean) : Bar

In the failing test above, the first encodeToString() always provides the correct output, and the second always matches it, implying some kind of caching may be to blame.

Expected behavior

Serializing BarBoolean in the example above should never produce a field from FooBoolean. Failing that, the presense of namingStrategy should not alter this behaviour.

If neither of the above are practical, then the order in which each class is first serialized should not affect the output.

Environment

  • Kotlin version: 2.0.20
  • Library version: 1.8.1
  • Kotlin platforms: JVM
  • Gradle version: 8.6

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions