Skip to content

Invalid json: Type descriminator is output in json array literals when adding unrelated polymophic serialized property to class #2164

@spand

Description

@spand

Describe the bug

This is output in the following "tags":["type":"kotlin.collections.ArrayList","2323"]

{"body":{"foo1":{"number":1,"stringOrInt":5},"name":"2","tags":["type":"kotlin.collections.ArrayList","2323"],"foo2":{"number":1,"stringOrInt":5}}}

Expected behavior
If I remove the stringOrInt property this is output:

{"body":{"foo1":{"number":1},"name":"2","tags":["2323"],"foo2":{"number":1}}}

It is not visible in my reduced case here but in my real case the equivalent of Foo instances also got a type descriminator in the json even though it should not be needed.

To Reproduce


import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.serializer
import java.io.ByteArrayOutputStream
import kotlin.test.Test

class JsonTest {

    private val format = Json {
        prettyPrint = false
        serializersModule = SerializersModule {
            polymorphic(SealedBase::class) {
                subclass(SealedInt::class, SealedIntSerializer)
                subclass(SealedString::class, SealedStringSerializer)
            }
        }
    }

    object SealedIntSerializer : JsonTransformingSerializer<SealedInt>(serializer()) {
        override fun transformSerialize(element: JsonElement): JsonElement {
            if (element is JsonObject) {
                val imageElement = element.getValue("image")
                return super.transformSerialize(imageElement)
            }
            return super.transformSerialize(element)
        }
    }

    object SealedStringSerializer : JsonTransformingSerializer<SealedString>(serializer()) {
        override fun transformSerialize(element: JsonElement): JsonElement {
            if (element is JsonObject) {
                val imageElement = element.getValue("image")
                return super.transformSerialize(imageElement)
            }
            return super.transformSerialize(element)
        }
    }

    @Test
    fun foo() {
        val card = BodyType.Foo(
            1,
            SealedInt(5),
        )
        val apiSquadResponseSuccess = Envelope(BodyType(card, "2", listOf("2323"), card))

        val baos = ByteArrayOutputStream()
        format.encodeToStream(Envelope.serializer(), apiSquadResponseSuccess, baos)
        println(baos.toString(Charsets.UTF_8))
    }
}

@Serializable
data class Envelope(
    val body: BodyType,
)

@Serializable
data class BodyType(
    val foo1: Foo,
    val name: String?,
    val tags: List<String>,
    val foo2: Foo,
) {
    @Serializable
    data class Foo(
        val number: Int,
        val stringOrInt: SealedBase,
    )

}

@Serializable
abstract class SealedBase

@Serializable
data class SealedString(val image: String) : SealedBase()

@Serializable
data class SealedInt(val image: Int) : SealedBase()


Environment

  • Kotlin version: 1.7.10
  • Library version: 1.4.1
  • Kotlin platforms: JVM

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions