Skip to content

Commit b43a654

Browse files
authored
Make type argument in JsonTransformingSerializer nullable (#2911)
This will allow for more flexible Json transformations, replacing invalid/unexpected input data with `null`s. Fixes #1927
1 parent 684cbb3 commit b43a654

File tree

3 files changed

+27
-2
lines changed

3 files changed

+27
-2
lines changed

formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTransformingSerializerTest.kt

+25
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,29 @@ class JsonTransformingSerializerTest : JsonTestBase() {
9898
assertEquals(correctExample, json.decodeFromString(DocExample.serializer(), """{"data":["str1"]}""", streaming))
9999
assertEquals(correctExample, json.decodeFromString(DocExample.serializer(), """{"data":"str1"}""", streaming))
100100
}
101+
102+
// Wraps/unwraps {"data":null} to just `null`, because StringData.data is not nullable
103+
object NullableStringDataSerializer : JsonTransformingSerializer<StringData?>(StringData.serializer().nullable) {
104+
override fun transformDeserialize(element: JsonElement): JsonElement {
105+
val data = element.jsonObject["data"]?.jsonPrimitive
106+
if (data?.contentOrNull.isNullOrBlank()) return JsonNull
107+
return element
108+
}
109+
110+
override fun transformSerialize(element: JsonElement): JsonElement {
111+
return if (element is JsonNull) JsonObject(mapOf("data" to JsonNull))
112+
else element
113+
}
114+
}
115+
116+
@Serializable
117+
data class NullableStringDataHolder(@Serializable(NullableStringDataSerializer::class) val stringData: StringData?)
118+
119+
@Test
120+
fun testNullableTransformingSerializer() {
121+
val normalInput = """{"stringData":{"data":"str1"}}"""
122+
val nullInput = """{"stringData":{"data":null}}"""
123+
assertJsonFormAndRestored(NullableStringDataHolder.serializer(), NullableStringDataHolder(StringData("str1")), normalInput)
124+
assertJsonFormAndRestored(NullableStringDataHolder.serializer(), NullableStringDataHolder(null), nullInput)
125+
}
101126
}

formats/json/api/kotlinx-serialization-json.klib.api

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ abstract class <#A: kotlin/Any> kotlinx.serialization.json/JsonContentPolymorphi
111111
final fun serialize(kotlinx.serialization.encoding/Encoder, #A) // kotlinx.serialization.json/JsonContentPolymorphicSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:0){}[0]
112112
}
113113

114-
abstract class <#A: kotlin/Any> kotlinx.serialization.json/JsonTransformingSerializer : kotlinx.serialization/KSerializer<#A> { // kotlinx.serialization.json/JsonTransformingSerializer|null[0]
114+
abstract class <#A: kotlin/Any?> kotlinx.serialization.json/JsonTransformingSerializer : kotlinx.serialization/KSerializer<#A> { // kotlinx.serialization.json/JsonTransformingSerializer|null[0]
115115
constructor <init>(kotlinx.serialization/KSerializer<#A>) // kotlinx.serialization.json/JsonTransformingSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>){}[0]
116116

117117
open val descriptor // kotlinx.serialization.json/JsonTransformingSerializer.descriptor|{}descriptor[0]

formats/json/commonMain/src/kotlinx/serialization/json/JsonTransformingSerializer.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import kotlinx.serialization.json.internal.*
5252
* Should be able to parse [JsonElement] from [transformDeserialize] function.
5353
* Usually, default [serializer] is sufficient.
5454
*/
55-
public abstract class JsonTransformingSerializer<T : Any>(
55+
public abstract class JsonTransformingSerializer<T : Any?>(
5656
private val tSerializer: KSerializer<T>
5757
) : KSerializer<T> {
5858

0 commit comments

Comments
 (0)