正在使用的版本
:2.0.20kotlin
:2.0.20kotlinx-serialization
:0.5.4kotlinx-io
我一直遵循
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
可以依次包含:
|int |int |short |short |
|bbbb |bbbb |bb |bb |
|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
使用最后/剩余字段的默认值
您需要做几件事。
首先,目前
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 字节情况下解码。
当然请注意,当在这些情况之外提供缓冲区时,这不考虑错误处理;您可能需要考虑一种方法。