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

json: Use @SerialName of inline polymorphic children #2601

Merged
merged 2 commits into from Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,51 @@
/*
* Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.serialization.features.sealed

import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlin.jvm.*
import kotlin.test.*

class SealedInterfacesInlineSerialNameTest : JsonTestBase() {
@Serializable
data class Child1Value(
val a: Int,
val b: String
)

@Serializable
data class Child2Value(
val c: Int,
val d: String
)

@Serializable
sealed interface Parent

@Serializable
@SerialName("child1")
@JvmInline
value class Child1(val value: Child1Value) : Parent

@Serializable
@SerialName("child2")
@JvmInline
value class Child2(val value: Child2Value) : Parent

// From https://github.com/Kotlin/kotlinx.serialization/issues/2288
@Test
fun testSealedInterfaceInlineSerialName() {
val messages = listOf(
Child1(Child1Value(1, "one")),
Child2(Child2Value(2, "two"))
)
assertJsonFormAndRestored(
serializer(),
messages,
"""[{"type":"child1","a":1,"b":"one"},{"type":"child2","c":2,"d":"two"}]"""
)
}
}
Expand Up @@ -43,6 +43,7 @@ internal class StreamingJsonEncoder(
// Forces serializer to wrap all values into quotes
private var forceQuoting: Boolean = false
private var polymorphicDiscriminator: String? = null
private var polymorphicSerialName: String? = null

init {
val i = mode.ordinal
Expand All @@ -66,12 +67,12 @@ internal class StreamingJsonEncoder(
}
}

private fun encodeTypeInfo(descriptor: SerialDescriptor) {
private fun encodeTypeInfo(discriminator: String, serialName: String) {
composer.nextItem()
encodeString(polymorphicDiscriminator!!)
encodeString(discriminator)
composer.print(COLON)
composer.space()
encodeString(descriptor.serialName)
encodeString(serialName)
}

override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
Expand All @@ -81,9 +82,11 @@ internal class StreamingJsonEncoder(
composer.indent()
}

if (polymorphicDiscriminator != null) {
encodeTypeInfo(descriptor)
val discriminator = polymorphicDiscriminator
if (discriminator != null) {
encodeTypeInfo(discriminator, polymorphicSerialName ?: descriptor.serialName)
polymorphicDiscriminator = null
polymorphicSerialName = null
}

if (mode == newMode) {
Expand Down Expand Up @@ -160,6 +163,7 @@ internal class StreamingJsonEncoder(
when {
descriptor.isUnsignedNumber -> StreamingJsonEncoder(composerAs(::ComposerForUnsignedNumbers), json, mode, null)
descriptor.isUnquotedLiteral -> StreamingJsonEncoder(composerAs(::ComposerForUnquotedLiterals), json, mode, null)
polymorphicDiscriminator != null -> apply { polymorphicSerialName = descriptor.serialName }
else -> super.encodeInline(descriptor)
}

Expand Down
Expand Up @@ -35,7 +35,8 @@ internal fun <T> Json.readPolymorphicJson(

private sealed class AbstractJsonTreeDecoder(
override val json: Json,
open val value: JsonElement
open val value: JsonElement,
protected val polymorphicDiscriminator: String? = null
) : NamedValueDecoder(), JsonDecoder {

override val serializersModule: SerializersModule
Expand Down Expand Up @@ -63,7 +64,7 @@ private sealed class AbstractJsonTreeDecoder(
{ JsonTreeMapDecoder(json, cast(currentObject, descriptor)) },
{ JsonTreeListDecoder(json, cast(currentObject, descriptor)) }
)
else -> JsonTreeDecoder(json, cast(currentObject, descriptor))
else -> JsonTreeDecoder(json, cast(currentObject, descriptor), polymorphicDiscriminator)
}
}

Expand Down Expand Up @@ -159,11 +160,15 @@ private sealed class AbstractJsonTreeDecoder(

override fun decodeInline(descriptor: SerialDescriptor): Decoder {
return if (currentTagOrNull != null) super.decodeInline(descriptor)
else JsonPrimitiveDecoder(json, value).decodeInline(descriptor)
else JsonPrimitiveDecoder(json, value, polymorphicDiscriminator).decodeInline(descriptor)
}
}

private class JsonPrimitiveDecoder(json: Json, override val value: JsonElement) : AbstractJsonTreeDecoder(json, value) {
private class JsonPrimitiveDecoder(
json: Json,
override val value: JsonElement,
polymorphicDiscriminator: String? = null
) : AbstractJsonTreeDecoder(json, value, polymorphicDiscriminator) {

init {
pushTag(PRIMITIVE_TAG)
Expand All @@ -180,9 +185,9 @@ private class JsonPrimitiveDecoder(json: Json, override val value: JsonElement)
private open class JsonTreeDecoder(
json: Json,
override val value: JsonObject,
private val polyDiscriminator: String? = null,
polymorphicDiscriminator: String? = null,
private val polyDescriptor: SerialDescriptor? = null
) : AbstractJsonTreeDecoder(json, value) {
) : AbstractJsonTreeDecoder(json, value, polymorphicDiscriminator) {
private var position = 0
private var forceNull: Boolean = false
/*
Expand Down Expand Up @@ -251,7 +256,7 @@ private open class JsonTreeDecoder(
// in endStructure can filter polyDiscriminator out.
if (descriptor === polyDescriptor) {
return JsonTreeDecoder(
json, cast(currentObject(), polyDescriptor), polyDiscriminator, polyDescriptor
json, cast(currentObject(), polyDescriptor), polymorphicDiscriminator, polyDescriptor
)
}

Expand All @@ -271,7 +276,7 @@ private open class JsonTreeDecoder(
}

for (key in value.keys) {
if (key !in names && key != polyDiscriminator) {
if (key !in names && key != polymorphicDiscriminator) {
throw UnknownKeyException(key, value.toString())
}
}
Expand Down
Expand Up @@ -35,6 +35,7 @@ private sealed class AbstractJsonTreeEncoder(
protected val configuration = json.configuration

private var polymorphicDiscriminator: String? = null
private var polymorphicSerialName: String? = null

override fun elementName(descriptor: SerialDescriptor, index: Int): String =
descriptor.getJsonElementName(json, index)
Expand Down Expand Up @@ -112,8 +113,12 @@ private sealed class AbstractJsonTreeEncoder(
}

override fun encodeInline(descriptor: SerialDescriptor): Encoder {
return if (currentTagOrNull != null) super.encodeInline(descriptor)
else JsonPrimitiveEncoder(json, nodeConsumer).encodeInline(descriptor)
return if (currentTagOrNull != null) {
if (polymorphicDiscriminator != null) polymorphicSerialName = descriptor.serialName
super.encodeInline(descriptor)
} else {
JsonPrimitiveEncoder(json, nodeConsumer).encodeInline(descriptor)
}
}

@SuppressAnimalSniffer // Long(Integer).toUnsignedString(long)
Expand Down Expand Up @@ -148,9 +153,11 @@ private sealed class AbstractJsonTreeEncoder(
else -> JsonTreeEncoder(json, consumer)
}

if (polymorphicDiscriminator != null) {
encoder.putElement(polymorphicDiscriminator!!, JsonPrimitive(descriptor.serialName))
val discriminator = polymorphicDiscriminator
if (discriminator != null) {
encoder.putElement(discriminator, JsonPrimitive(polymorphicSerialName ?: descriptor.serialName))
polymorphicDiscriminator = null
polymorphicSerialName = null
}

return encoder
Expand Down