summaryrefslogtreecommitdiff
path: root/formats/json
diff options
context:
space:
mode:
authorLeonid Startsev <sandwwraith@users.noreply.github.com>2021-12-15 18:55:06 +0300
committerGitHub <noreply@github.com>2021-12-15 18:55:06 +0300
commita33ef021ae479949a7663cd82cfbc32026a2c9de (patch)
tree5d90f45164f1211fb2bd2cf9f4d204271d6c05e6 /formats/json
parent1b2344f3254a234324e406f790bb7335c5bd397a (diff)
downloadkotlinx.serialization-a33ef021ae479949a7663cd82cfbc32026a2c9de.tar.gz
Properly handle top-level value classes in encodeToJsonElement (#1777)
Fixes #1774
Diffstat (limited to 'formats/json')
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt7
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/WriteMode.kt10
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineClassesTest.kt56
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"}"""
)
}