summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeonid Startsev <sandwwraith@users.noreply.github.com>2022-06-17 22:43:27 +0200
committerGitHub <noreply@github.com>2022-06-17 22:43:27 +0200
commit6a5ebd555017dad02ab5c085fde68f98c25415b3 (patch)
tree16a6eb735df00dc1bbfa5d5563e3bf24a5a7e479
parent4b6410019b03c0f816ea75cd8f3ee6b25341e2d9 (diff)
downloadkotlinx.serialization-6a5ebd555017dad02ab5c085fde68f98c25415b3.tar.gz
Support quoting unsigned integers when used as map keys (#1969)
Fixes #1966
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/Composers.kt18
-rw-r--r--formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.kt12
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/inline/EncodeInlineElementTest.kt9
-rw-r--r--formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineMapQuotedTest.kt66
4 files changed, 88 insertions, 17 deletions
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/Composers.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/Composers.kt
index 2bc080f9..d3860d21 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/Composers.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/Composers.kt
@@ -1,13 +1,13 @@
/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@file:OptIn(ExperimentalSerializationApi::class)
package kotlinx.serialization.json.internal
-import kotlinx.serialization.ExperimentalSerializationApi
-import kotlinx.serialization.json.Json
-import kotlin.jvm.JvmField
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlin.jvm.*
internal fun Composer(sb: JsonStringBuilder, json: Json): Composer =
if (json.configuration.prettyPrint) ComposerWithPrettyPrint(sb, json) else Composer(sb)
@@ -42,21 +42,21 @@ internal open class Composer(@JvmField internal val sb: JsonStringBuilder) {
}
@ExperimentalUnsignedTypes
-internal class ComposerForUnsignedNumbers(sb: JsonStringBuilder) : Composer(sb) {
+internal class ComposerForUnsignedNumbers(sb: JsonStringBuilder, private val forceQuoting: Boolean) : Composer(sb) {
override fun print(v: Int) {
- return super.print(v.toUInt().toString())
+ if (forceQuoting) printQuoted(v.toUInt().toString()) else print(v.toUInt().toString())
}
override fun print(v: Long) {
- return super.print(v.toULong().toString())
+ if (forceQuoting) printQuoted(v.toULong().toString()) else print(v.toULong().toString())
}
override fun print(v: Byte) {
- return super.print(v.toUByte().toString())
+ if (forceQuoting) printQuoted(v.toUByte().toString()) else print(v.toUByte().toString())
}
override fun print(v: Short) {
- return super.print(v.toUShort().toString())
+ if (forceQuoting) printQuoted(v.toUShort().toString()) else print(v.toUShort().toString())
}
}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.kt
index cdaeeb03..0381a137 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.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-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.serialization.json.internal
@@ -160,10 +160,18 @@ internal class StreamingJsonEncoder(
override fun encodeInline(inlineDescriptor: SerialDescriptor): Encoder =
if (inlineDescriptor.isUnsignedNumber) StreamingJsonEncoder(
- ComposerForUnsignedNumbers(composer.sb), json, mode, null
+ composerForUnsignedNumbers(), json, mode, null
)
else super.encodeInline(inlineDescriptor)
+ private fun composerForUnsignedNumbers(): Composer {
+ // If we're inside encodeInline().encodeSerializableValue, we should preserve the forceQuoting state
+ // inside the composer, but not in the encoder (otherwise we'll get into `if (forceQuoting) encodeString(value.toString())` part
+ // and unsigned numbers would be encoded incorrectly)
+ return if (composer is ComposerForUnsignedNumbers) composer
+ else ComposerForUnsignedNumbers(composer.sb, forceQuoting)
+ }
+
override fun encodeNull() {
composer.print(NULL)
}
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/inline/EncodeInlineElementTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/inline/EncodeInlineElementTest.kt
index 037d7858..5c53be0a 100644
--- a/formats/json/commonTest/src/kotlinx/serialization/features/inline/EncodeInlineElementTest.kt
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/inline/EncodeInlineElementTest.kt
@@ -1,20 +1,17 @@
/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@file:OptIn(ExperimentalUnsignedTypes::class)
-/*
- * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
package kotlinx.serialization.features.inline
import kotlinx.serialization.*
import kotlinx.serialization.builtins.*
-import kotlinx.serialization.encoding.*
import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
import kotlinx.serialization.test.*
-import kotlin.test.Test
+import kotlin.test.*
@Serializable(WithUnsignedSerializer::class)
data class WithUnsigned(val u: UInt)
diff --git a/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineMapQuotedTest.kt b/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineMapQuotedTest.kt
new file mode 100644
index 00000000..53461bfd
--- /dev/null
+++ b/formats/json/commonTest/src/kotlinx/serialization/features/inline/InlineMapQuotedTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features.inline
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.test.*
+import kotlin.jvm.*
+import kotlin.test.*
+
+class InlineMapQuotedTest : JsonTestBase() {
+ @Serializable(with = CustomULong.Serializer::class)
+ data class CustomULong(val value: ULong) {
+ @OptIn(ExperimentalSerializationApi::class, ExperimentalUnsignedTypes::class)
+ internal object Serializer : KSerializer<CustomULong> {
+ override val descriptor: SerialDescriptor =
+ @OptIn(ExperimentalUnsignedTypes::class) ULong.serializer().descriptor
+
+ override fun deserialize(decoder: Decoder): CustomULong =
+ CustomULong(decoder.decodeInline(descriptor).decodeSerializableValue(ULong.serializer()))
+
+ override fun serialize(encoder: Encoder, value: CustomULong) {
+ encoder.encodeInline(descriptor).encodeSerializableValue(ULong.serializer(), value.value)
+ }
+ }
+ }
+
+ @JvmInline
+ @Serializable
+ value class WrappedLong(val value: Long)
+
+ @JvmInline
+ @Serializable
+ value class WrappedULong(val value: ULong)
+
+ @Serializable
+ data class Carrier(
+ val mapLong: Map<Long, Long>,
+ val mapULong: Map<ULong, Long>,
+ val wrappedLong: Map<WrappedLong, Long>,
+ val mapWrappedU: Map<WrappedULong, Long>,
+ val mapCustom: Map<CustomULong, Long>
+ )
+
+ @Test
+ fun testInlineClassAsMapKey() = noLegacyJs {
+ println(Long.MAX_VALUE.toULong() + 2UL)
+ val c = Carrier(
+ mapOf(1L to 1L),
+ mapOf(Long.MAX_VALUE.toULong() + 2UL to 2L),
+ mapOf(WrappedLong(3L) to 3L),
+ mapOf(WrappedULong(Long.MAX_VALUE.toULong() + 4UL) to 4L),
+ mapOf(CustomULong(Long.MAX_VALUE.toULong() + 5UL) to 5L)
+ )
+ assertJsonFormAndRestored(
+ serializer(),
+ c,
+ """{"mapLong":{"1":1},"mapULong":{"9223372036854775809":2},"wrappedLong":{"3":3},"mapWrappedU":{"9223372036854775811":4},"mapCustom":{"9223372036854775812":5}}"""
+ )
+ }
+}