Kotlinx Serialization - 忽略 null 值的自定义序列化器

13
假设我有一个像这样的类:
@Serializable
data class MyClass(
    @SerialName("a") val a: String?,
    @SerialName("b") val b: String
)

假设 anullb 的值为 "b value",那么执行 Json.stringify(MyClass.serializer(), this) 将会产生:
{ "a": null, "b": "b value" }

基本上,如果anull,我想得到这个:

{ "b": "b value" }

根据我的一些研究,目前Kotlinx序列化不支持自定义忽略null值的序列化器。因此,我正在尝试构建一个自定义序列化器来实现此功能。我遵循了这里的指南,但是无法正确构建。

请问有人能为我提供帮助吗?谢谢。

6个回答

11
你可以使用 explicitNulls = false 例如:
@OptIn(ExperimentalSerializationApi::class)
val format = Json { explicitNulls = false }
    
@Serializable
data class Project(
    val name: String,
    val language: String,
    val version: String? = "1.3.0",
    val website: String?,
)
    
fun main() {
    val data = Project("kotlinx.serialization", "Kotlin", null, null)
    val json = format.encodeToString(data)
    println(json) // {"name":"kotlinx.serialization","language":"Kotlin"}
}

https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/json.md#explicit-nulls


4
JsonConfiguration中使用encodeDefaults = false属性,可以防止序列化空值(或其他可选值)。

1
这仅在您希望忽略所有默认值的情况下才起作用,但并非总是如此。一个例子是WooCommerce,如果不存在“variation_id”,则不应将其作为行项目传递给订单,因此理想的路径是将其设置为null,但WooCommerce不接受null作为值。在这种情况下,更好的选择是提供更细粒度的控制,比如说,“如果未设置此值,请忽略它”或“如果该值为空,请忽略它”。 - CodyEngel

2

由于我也曾为此苦苦挣扎,让我与您分享我找到的解决方案,它是基于属性的,不需要为整个类创建序列化器。

class ExcludeIfNullSerializer : KSerializer<String?> {

    override fun deserialize(decoder: Decoder): String {
        return decoder.decodeString()
    }

    override val descriptor: SerialDescriptor
        get() = PrimitiveSerialDescriptor("ExcludeNullString", PrimitiveKind.STRING)

    override fun serialize(encoder: Encoder, value: String?) {
        if (value != null) {
            encoder.encodeString(value)
        }
    }
}

将与以下类一起按预期工作
@Serializable
    class TestDto(
        @SerialName("someString")
        val someString: String,
        @SerialName("id")
        @EncodeDefault(EncodeDefault.Mode.NEVER)
        @Serializable(with = ExcludeIfNullSerializer::class)
        val id: String? = null
    )

请注意,@EncodeDefault(EncodeDefault.Mode.NEVER)在这里非常重要,以防您使用encodeDefaults = true的JsonBuilder,在这种情况下,即使id字段的值为null,序列化库仍将添加'id' json键,除非使用此注释。

2

尝试这个(未经测试,只是基于适应示例):

@Serializable
data class MyClass(val a: String?, val b: String) {
    @Serializer(forClass = MyClass::class)
        companion object : KSerializer<MyClass> {
        override val descriptor: SerialDescriptor = object : SerialClassDescImpl("MyClass") {
            init {
                addElement("a")
                addElement("b")
            }
        }

        override fun serialize(encoder: Encoder, obj: MyClass) {
            encoder.beginStructure(descriptor).run {
                obj.a?.let { encodeStringElement(descriptor, 0, obj.a) }
                encodeStringElement(descriptor, 1, obj.b)
                endStructure(descriptor)
            }
        }

        override fun deserialize(decoder: Decoder): MyClass {
            var a: String? = null
            var b = ""

            decoder.beginStructure(descriptor).run {
                loop@ while (true) {
                    when (val i = decodeElementIndex(descriptor)) {
                        CompositeDecoder.READ_DONE -> break@loop
                        0 -> a = decodeStringElement(descriptor, i)
                        1 -> b = decodeStringElement(descriptor, i)
                        else -> throw SerializationException("Unknown index $i")
                    }
                }
                endStructure(descriptor)
            }

            return MyClass(a, b)
        }
    }
}

1

JsonConfiguration已被弃用,推荐使用自 Json{} 构建器开始,自kotlinx.serialization 1.0.0-RC起,详情请参考更改日志

现在你需要像这样编写代码:

val json = Json { encodeDefaults = false }
val body = json.encodeToString(someSerializableObject)

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接