kotlinx.serialization icon indicating copy to clipboard operation
kotlinx.serialization copied to clipboard

Typealias of typealias of typealias loses serializer after upgrade to Kotlin 2

Open juriad opened this issue 5 months ago • 0 comments

Describe the bug

I have an alternative serializer for Map (produces [{key, value}, ...]; simplified for this report). It is part of another Map with the usual serializer. We use typealiases a lot to give meaningful names to Maps, Lists and similar. When upgrading from Kotlin 1.9 to 2.1 (and serialization from 1.5.1 to 1.8.1, also tried 1.9.0) I noticed a test failing. I am not sure if it is a bug caused by the compiler or the serialization library.

To Reproduce

import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.JsonTransformingSerializer
import kotlinx.serialization.json.jsonObject

object MapWithSizeSerializer : JsonTransformingSerializer<Map<String, String>>(kotlinx.serialization.serializer<Map<String, String>>()) {
    override fun transformSerialize(element: JsonElement): JsonElement {
        return JsonObject(element.jsonObject + ("size" to JsonPrimitive(element.jsonObject.size)))
    }
}

typealias SizedMap<K, V> = @Serializable(MapWithSizeSerializer::class) Map<K, V>
typealias StringSizedMap = SizedMap<String, String>
typealias MapOfSizedMaps = Map<String, StringSizedMap>
// typealias MapOfSizedMaps = Map<String, SizedMap<String, String>>

@Serializable
data class LanguageProficiency(
    val map: MapOfSizedMaps
)

fun main() {
    val languages = LanguageProficiency(
        mapOf(
            "A" to mapOf("A1" to "beginner", "A2" to "elementary"),
            "B" to mapOf("B1" to "intermediate", "B2" to "upper intermediate"),
            "C" to mapOf("C1" to "advanced", "C2" to "proficient"),
        )
    )
    val json = Json { ignoreUnknownKeys = true }
    println(json.encodeToString(LanguageProficiency.serializer(), languages))
}

Prints:

{"map":{"A":{"A1":"beginner","A2":"elementary"},"B":{"B1":"intermediate","B2":"upper intermediate"},"C":{"C1":"advanced","C2":"proficient"}}}

Expected behavior

{"map":{"A":{"A1":"beginner","A2":"elementary","size":2},"B":{"B1":"intermediate","B2":"upper intermediate","size":2},"C":{"C1":"advanced","C2":"proficient","size":2}}}

Uncomment the alternative definition of MapOfSizedMaps to fix it.

Environment

  • Kotlin version: 2.1.21 (upgraded from 1.9.20)
  • Library version: 1.8.1 (upgraded from 1.5.1)
  • Kotlin platforms: JVM
  • Gradle version: 8.5

juriad avatar Jul 08 '25 08:07 juriad