diff options
author | Leonid Startsev <sandwwraith@users.noreply.github.com> | 2021-12-15 18:55:06 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-15 18:55:06 +0300 |
commit | a33ef021ae479949a7663cd82cfbc32026a2c9de (patch) | |
tree | 5d90f45164f1211fb2bd2cf9f4d204271d6c05e6 /formats/json | |
parent | 1b2344f3254a234324e406f790bb7335c5bd397a (diff) | |
download | kotlinx.serialization-a33ef021ae479949a7663cd82cfbc32026a2c9de.tar.gz |
Properly handle top-level value classes in encodeToJsonElement (#1777)
Fixes #1774
Diffstat (limited to 'formats/json')
3 files changed, 63 insertions, 10 deletions
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt index f89f4da4..74f02bf4 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:OptIn(ExperimentalSerializationApi::class) @@ -69,7 +69,7 @@ private sealed class AbstractJsonTreeEncoder( override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) { // Writing non-structured data (i.e. primitives) on top-level (e.g. without any tag) requires special output - if (currentTagOrNull != null || serializer.descriptor.kind !is PrimitiveKind && serializer.descriptor.kind !== SerialKind.ENUM) { + if (currentTagOrNull != null || !serializer.descriptor.carrierDescriptor(serializersModule).requiresTopLevelTag) { encodePolymorphically(serializer, value) { polymorphicDiscriminator = it } } else JsonPrimitiveEncoder(json, nodeConsumer).apply { encodeSerializableValue(serializer, value) @@ -139,6 +139,9 @@ private sealed class AbstractJsonTreeEncoder( } } +private val SerialDescriptor.requiresTopLevelTag: Boolean + get() = kind is PrimitiveKind || kind === SerialKind.ENUM + internal const val PRIMITIVE_TAG = "primitive" // also used in JsonPrimitiveInput private class JsonPrimitiveEncoder( diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/WriteMode.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/WriteMode.kt index 9cd2a67b..382d8bce 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/WriteMode.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/WriteMode.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ @file:OptIn(ExperimentalSerializationApi::class) @@ -7,9 +7,9 @@ package kotlinx.serialization.json.internal import kotlinx.serialization.* import kotlinx.serialization.descriptors.* -import kotlinx.serialization.json.Json -import kotlinx.serialization.modules.SerializersModule -import kotlin.jvm.JvmField +import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* +import kotlin.jvm.* internal enum class WriteMode(@JvmField val begin: Char, @JvmField val end: Char) { OBJ(BEGIN_OBJ, END_OBJ), @@ -47,6 +47,6 @@ internal inline fun <T, R1 : T, R2 : T> Json.selectMapMode( internal fun SerialDescriptor.carrierDescriptor(module: SerializersModule): SerialDescriptor = when { kind == SerialKind.CONTEXTUAL -> module.getContextualDescriptor(this)?.carrierDescriptor(module) ?: this - isInline -> getElementDescriptor(0) + isInline -> getElementDescriptor(0).carrierDescriptor(module) else -> this } diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesTest.kt index 667094d4..1eedafae 100644 --- a/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesTest.kt +++ b/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesTest.kt @@ -15,6 +15,7 @@ import kotlinx.serialization.* import kotlinx.serialization.builtins.* import kotlinx.serialization.encoding.* import kotlinx.serialization.json.* +import kotlinx.serialization.modules.* import kotlinx.serialization.test.* import kotlin.jvm.* import kotlin.test.* @@ -72,12 +73,61 @@ value class ResourceId(val id: String) value class ResourceType(val type: String) @Serializable -data class ResourceIdentifier(val id: ResourceId, val type: ResourceType) +@JvmInline +value class ResourceKind(val kind: SampleEnum) + +@Serializable +data class ResourceIdentifier(val id: ResourceId, val type: ResourceType, val type2: ValueWrapper) + +@Serializable @JvmInline +value class ValueWrapper(val wrapped: ResourceType) class InlineClassesTest : JsonTestBase() { private val precedent: UInt = Int.MAX_VALUE.toUInt() + 10.toUInt() @Test + fun testTopLevel() = noLegacyJs { + assertJsonFormAndRestored( + ResourceType.serializer(), + ResourceType("foo"), + """"foo"""", + ) + } + + @Test + fun testTopLevelOverEnum() = noLegacyJs { + assertJsonFormAndRestored( + ResourceKind.serializer(), + ResourceKind(SampleEnum.OptionC), + """"OptionC"""", + ) + } + + @Test + fun testTopLevelWrapper() = noLegacyJs { + assertJsonFormAndRestored( + ValueWrapper.serializer(), + ValueWrapper(ResourceType("foo")), + """"foo"""", + ) + } + + @Test + fun testTopLevelContextual() = noLegacyJs { + val module = SerializersModule { + contextual<ResourceType>(ResourceType.serializer()) + } + val json = Json(default) { serializersModule = module } + assertJsonFormAndRestored( + ContextualSerializer(ResourceType::class), + ResourceType("foo"), + """"foo"""", + json + ) + } + + + @Test fun testSimpleContainer() = noLegacyJs { assertJsonFormAndRestored( SimpleContainerForUInt.serializer(), @@ -106,8 +156,8 @@ class InlineClassesTest : JsonTestBase() { fun testInlineClassesWithStrings() = noLegacyJs { assertJsonFormAndRestored( ResourceIdentifier.serializer(), - ResourceIdentifier(ResourceId("resId"), ResourceType("resType")), - """{"id":"resId","type":"resType"}""" + ResourceIdentifier(ResourceId("resId"), ResourceType("resType"), ValueWrapper(ResourceType("wrappedType"))), + """{"id":"resId","type":"resType","type2":"wrappedType"}""" ) } |