`kotlinx-serialization`:如何在自定义格式实现中处理默认值

问题描述 投票:0回答:1

背景

正在使用的版本

  • kotlin
    :2.0.20
  • kotlinx-serialization
    :2.0.20
  • kotlinx-io
    :0.5.4

我一直遵循

kotlinx-serialization
自定义格式指南来实现自定义二进制协议,但遇到了困难......

相关解码器从

kotlinx-io
Buffer
读取并创建
data class
实例

问题

解码器在大部分情况下都可以工作除了可选字段

我正在尝试将

Buffer
解码为这种类型:

@Serializable
data class Foo(
    val one: Int,
    val two: Int,
    val three: Short,
    val four: Short = 10, // notice the default value
)

因此

Buffer
可以依次包含:

  • 12 字节/4 个字段
|int  |int  |short |short |
|bbbb |bbbb |bb    |bb    |
  • 或 10 个字节/3 个字段
|int  |int  |short |
|bbbb |bbbb |bb    |

成功解码12字节情况,但10字节情况失败,并出现以下问题:

Buffer doesn't contain required number of bytes (size: 0, required: 2)
java.io.EOFException: Buffer doesn't contain required number of bytes (size: 0, required: 2)
    at kotlinx.io.Buffer.throwEof(Buffer.kt:163)
    at kotlinx.io.Buffer.readShort(Buffer.kt:103)
    at com.acme.corp.serde.BufferDecoder.decodeShort(BufferDecoder.kt:<line_number>)
    at kotlinx.serialization.encoding.AbstractDecoder.decodeShortElement(AbstractDecoder.kt:52)
    at com.acme.corp.serde.OptionalFieldTest$Foo$$serializer.deserialize(OptionalFieldTest.kt:<line_number>)

测试用例

class OptionalFieldTest {
    @Test
    fun `options fields default when not enough bytes are available`() {
        val buffer =
            Buffer().apply {
                writeInt(1)
                writeInt(2)
                writeShort(3)
                // notice missing writeShort() here
            }
        val decoder = BufferDecoder(buffer)
        val decoded = decoder.decodeSerializableValue(serializer(), null)
        println("decoded into value=$decoded")
    }
}

解码实现

class BufferDecoder(
    private val buffer: Buffer
    private var elementsCount: Int = 0,
) : AbstractDecoder() {
    private var elementIndex = 0
    override val serializersModule: SerializersModule = EmptySerializersModule()

    override fun decodeBoolean(): Boolean = buffer.readByte().toInt() != 0

    override fun decodeByte(): Byte = buffer.readByte()

    override fun decodeShort(): Short = buffer.readShort()

    override fun decodeInt(): Int = buffer.readInt()

    override fun decodeLong(): Long = buffer.readLong()

    override fun decodeFloat(): Float = buffer.readFloat()

    override fun decodeDouble(): Double = buffer.readDouble()

    override fun decodeChar(): Char = buffer.readByte().toInt().toChar()

    override fun decodeString(): String = buffer.readString()

    override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeByte().toInt()

    override fun decodeSequentially(): Boolean = true

    override fun decodeElementIndex(descriptor: SerialDescriptor): Int =
        if (elementIndex == elementsCount) {
            CompositeDecoder.DECODE_DONE
        } else {
            elementIndex++
        }

    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
        BufferDecoder(
            buffer = buffer,
            elementsCount = descriptor.elementsCount,
        )
}

问题

如果

kotlinx-serialization
已耗尽,我不确定如何告诉
Buffer
使用最后/剩余字段的默认值

kotlin kotlinx.serialization
1个回答
0
投票

您需要做几件事。

首先,目前

decodeElementIndex
函数根本没有被调用,因为
decodeSequentially
返回
true
。有了这个
true
,解码器将被调用每个预期的
Foo
字段(即四次),而不管
decodeElementIndex
中的任何内容,而根本没有被调用。 (这是顺序解码协议。)

所以让我们发挥

decodeElementIndex
的作用。

override fun decodeSequentially(): Boolean = false

现在如问题中所写,当

decodeElementIndex
等于
elementIndex
时,
elementsCount
将导致反序列化停止——即,当对
Foo
的全部预期字段(即四个)进行解码函数调用时。但我们也希望它在缓冲区为空时停止,所以我们这样修改它:

override fun decodeElementIndex(descriptor: SerialDescriptor): Int =
    when {
        buffer.exhausted() -> CompositeDecoder.DECODE_DONE
        elementIndex == descriptor.elementsCount -> CompositeDecoder.DECODE_DONE
        else -> elementIndex++
    }

现在解码应该在短缓冲区情况下停止,并且

Foo
将在 10 和 12 字节情况下解码。

当然请注意,当在这些情况之外提供缓冲区时,这不考虑错误处理;您可能需要考虑一种方法。

© www.soinside.com 2019 - 2024. All rights reserved.