hon*_*ert 4 generics serialization kotlin kotlinx.serialization
我有一个为网络流量序列化的类。
@Serializable
data class Packet(val dataType: String, val payload: Any)
Run Code Online (Sandbox Code Playgroud)
我已经使用 Java 序列化通过网络发送它。接收器无法知道有效负载的类型,但 Java 反序列化它就好了,然后我when(dataType)用作查找以将Any对象正确地转换为正确的类型。轻松活泼。
但是 Kotlinx Serialization(带有 ProtoBuf)是这种Any类型的坚持者,原因对我来说并不明显。我无法为Any. 在文档中,他们推荐了一种多态方法,这可以正常工作,但您必须输入数据包:
data class Packet<out T : Any>(val dataType: String, val payload: T) : SomeBaseClass<T>
Run Code Online (Sandbox Code Playgroud)
但这有点糟糕,因为它使用内联具体化类型压低了许多代码路径,而且这并不能解决接收端不知道尝试反序列化有效负载的类型而无需先查看dataType字段的问题。
这是最糟糕的 catch-22。该框架不会忽略该payload: Any字段(给出编译错误),我什至无法编写自定义序列化程序,因为在客户序列化程序中定义element类型Any(用于描述符)会产生相同的运行时错误“未注册序列化程序”为Any。”
我已经使用 Java 序列化通过网络发送它。接收器无法知道有效负载的类型,但 Java 反序列化它就好了,然后我使用 when(dataType) 作为查找将 Any 对象正确转换为其正确类型。轻松活泼。
这是因为 java 序列化相当原始 - 只有一种方法可以序列化(并因此反序列化)一个对象。在 kotlinx.serialization 中,每个类都可以有自己的序列化策略(甚至几个)。这种灵活性是有代价的。Any可以处理序列化(对于其子类的声明列表),但是dataType在一般情况下,基于部分反序列化对象的字段动态确定反序列化策略是不可能的,因为不能保证dataType字段首先被反序列化。一些序列化格式(如 JSON 或 Protobuf)具有无序模式。可能会发生payload之前即将被反序列化的情况dataType,并且解码器接口不允许返回/进行多次传递。
如果您确定序列化格式/消息中的属性顺序(或者只是觉得很幸运),您可以使用以下自定义序列化程序:
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
@Serializable(with = PacketSerializer::class)
data class Packet(val dataType: String, val payload: Any)
object PacketSerializer : KSerializer<Packet> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Packet") {
element("dataType", serialDescriptor<String>())
element("payload", buildClassSerialDescriptor("Any"))
}
@Suppress("UNCHECKED_CAST")
private val dataTypeSerializers: Map<String, KSerializer<Any>> =
mapOf(
"String" to serializer<String>(),
"Int" to serializer<Int>(),
//list them all
).mapValues { (_, v) -> v as KSerializer<Any> }
private fun getPayloadSerializer(dataType: String): KSerializer<Any> = dataTypeSerializers[dataType]
?: throw SerializationException("Serializer for class $dataType is not registered in PacketSerializer")
override fun serialize(encoder: Encoder, value: Packet) {
encoder.encodeStructure(descriptor) {
encodeStringElement(descriptor, 0, value.dataType)
encodeSerializableElement(descriptor, 1, getPayloadSerializer(value.dataType), value.payload)
}
}
@ExperimentalSerializationApi
override fun deserialize(decoder: Decoder): Packet = decoder.decodeStructure(descriptor) {
if (decodeSequentially()) {
val dataType = decodeStringElement(descriptor, 0)
val payload = decodeSerializableElement(descriptor, 1, getPayloadSerializer(dataType))
Packet(dataType, payload)
} else {
require(decodeElementIndex(descriptor) == 0) { "dataType field should precede payload field" }
val dataType = decodeStringElement(descriptor, 0)
val payload = when (val index = decodeElementIndex(descriptor)) {
1 -> decodeSerializableElement(descriptor, 1, getPayloadSerializer(dataType))
CompositeDecoder.DECODE_DONE -> throw SerializationException("payload field is missing")
else -> error("Unexpected index: $index")
}
Packet(dataType, payload)
}
}
}
Run Code Online (Sandbox Code Playgroud)