Kotlinx Serialization - пользовательский сериализатор для игнорирования нулевого значения

Скажем, у меня есть такой класс:

@Serializable
data class MyClass(
    @SerialName("a") val a: String?,
    @SerialName("b") val b: String
)

Предположим, что a является null а также bзначение "b значение", тогда Json.stringify(MyClass.serializer(), this) производит:

{ "a": null, "b": "b value" }

В основном если a является null, Я хотел получить вот что:

{ "b": "b value" }

Из некоторых исследований я обнаружил, что в настоящее время это невозможно из коробки с сериализацией Kotlinx, поэтому я пытался создать собственный сериализатор, чтобы явно игнорировать nullценность. Я следовал руководству отсюда, но не смог составить правильный.

Может кто-нибудь пролить свет на меня? Спасибо.

6 ответов

Решение

Попробуйте это (не проверено, просто на основе адаптации примера):

@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)
        }
    }
}

Вы можете использовать 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

Использовать encodeDefaults = false собственность в JsonConfiguration и он не будет сериализовать нули (или другие необязательные значения)

Поскольку я также боролся с этим, позвольте мне поделиться с вами решением, которое я нашел для каждого свойства и не требует создания сериализатора для всего класса.

      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) имеет решающее значение здесь, если вы используете JsonBuilder с encodeDefaults = true, так как в этом случае библиотека сериализации все равно добавит ключ json «id», даже если значение поля id равно null, если только используя эту аннотацию.

JsonConfigurationявляется устаревшим в пользу Json {}builder начиная с kotlinx.serialization 1.0.0-RC согласно его журналу изменений.

Теперь вам нужно написать вот так:

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

На данный момент для всех, кто видит этот пост сегодня, значения по умолчанию не сериализованы (см. https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/basic-serialization.md#defaults-are-not- закодировано по умолчанию)

Таким образом, вы просто добавляете, чтобы установить нулевое значение по умолчанию, и оно не будет сериализовано.

Другие вопросы по тегам