diff options
author | Leonid Startsev <sandwwraith@users.noreply.github.com> | 2022-06-17 22:43:27 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-17 22:43:27 +0200 |
commit | 6a5ebd555017dad02ab5c085fde68f98c25415b3 (patch) | |
tree | 16a6eb735df00dc1bbfa5d5563e3bf24a5a7e479 | |
parent | 4b6410019b03c0f816ea75cd8f3ee6b25341e2d9 (diff) | |
download | kotlinx.serialization-6a5ebd555017dad02ab5c085fde68f98c25415b3.tar.gz |
Support quoting unsigned integers when used as map keys (#1969)
Fixes #1966
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}}""" + ) + } +} |