具有多态 kotlinx 序列化的自定义序列化程序
Posted
技术标签:
【中文标题】具有多态 kotlinx 序列化的自定义序列化程序【英文标题】:Custom serializer with polymorphic kotlinx serialization 【发布时间】:2020-02-15 13:29:21 【问题描述】:有了kotlinx.serialization多态,我想得到
"type":"veh_t","owner":"Ivan","bodyType":"cistern","carryingCapacityInTons":5,"detachable":false
但我明白了
"type":"kotlin.collections.LinkedHashMap","owner":"Ivan","bodyType":"cistern","carryingCapacityInTons":5,"detachable":false
我使用以下型号
interface Vehicle
val owner: String
@Serializable
@SerialName("veh_p")
data class PassengerCar(
override val owner: String,
val numberOfSeats: Int
) : Vehicle
@Serializable
@SerialName("veh_t")
data class Truck(
override val owner: String,
val body: Body
) : Vehicle
@Serializable
data class Body(
val bodyType: String,
val carryingCapacityInTons: Int,
val detachable: Boolean
//a lot of other fields
)
我应用以下Json
inline val VehicleJson: Json get() = Json(context = SerializersModule
polymorphic(Vehicle::class)
PassengerCar::class with PassengerCar.serializer()
Truck::class with TruckKSerializer
)
我使用序列化器 TruckKSerializer 因为服务器采用扁平结构。同时,在应用程序中我想使用一个对象 Truck.Body。对于扁平化,我根据这些类中的文档使用 JsonOutput 和 JsonInput 在 Serializator 中覆盖 fun serialize(encoder: Encoder, obj : T)
和 fun deserialize(decoder: Decoder): T
。
object TruckKSerializer : KSerializer<Truck>
override val descriptor: SerialDescriptor = SerialClassDescImpl("Truck")
override fun serialize(encoder: Encoder, obj: Truck)
val output = encoder as? JsonOutput ?: throw SerializationException("This class can be saved only by Json")
output.encodeJson(json
obj::owner.name to obj.owner
encoder.json.toJson(Truck.Body.serializer(), obj.body)
.jsonObject.content
.forEach (name, value) ->
name to value
)
@ImplicitReflectionSerializer
override fun deserialize(decoder: Decoder): Truck
val input = decoder as? JsonInput
?: throw SerializationException("This class can be loaded only by Json")
val tree = input.decodeJson() as? JsonObject
?: throw SerializationException("Expected JsonObject")
return Truck(
tree.getPrimitive("owner").content,
VehicleJson.fromJson<Truck.Body>(tree)
)
最后,我使用stringify(serializer: SerializationStrategy<T>, obj: T)
VehicleJson.stringify(
PolymorphicSerializer(Vehicle::class),
Truck(
owner = "Ivan",
body = Truck.Body(
bodyType = "cistern",
carryingCapacityInTons = 5,
detachable = false
)
)
)
我最终得到"type":"kotlin.collections.LinkedHashMap", ...
,但我需要"type":"veh_t", ...
如何获得正确的类型?我想对Vehicle
使用多态性并使用 Truck.Body.serializer() 对 Body 对象进行编码以展平。
通过这种序列化,PassengerCar 类运行良好。
VehicleJson.stringify(
PolymorphicSerializer(Vehicle::class),
PassengerCar(
owner = "Oleg",
numberOfSeats = 4
)
)
结果正确:
"type":"veh_p","owner":"Oleg","numberOfSeats":4
我认为问题在于自定义序列化程序TruckKSerializer
。
我注意到如果我在覆盖的fun serialize(encoder: Encoder, obj : T)
下一个代码中使用
encoder
.beginStructure(descriptor)
.apply
//...
.endStructure(descriptor)
我得到了正确的类型,但无法使用其序列化器将对象 Truck.Body 展平。
【问题讨论】:
【参考方案1】:打开和关闭复合的正确方法
这是代码吗
val composite = encoder.beginStructure(descriptor)
// use composite instead of encoder here
composite.endStructure(descriptor)
你应该能够使用.encodeSerializable(Body.serializer(), body)
序列化Body
并始终传递描述符,否则它将退回到 json 字典的 LinkedhashMap 之类的东西
【讨论】:
在这种情况下,您必须在描述符中使用fun encodeStringElement (desc: SerialDescriptor, index: Int, value: String)
或fun <T : Any?> encodeSerializableElement(desc: SerialDescriptor, index: Int, serializer: SerializationStrategy<T>, value: T)
或其他方式指定元素的位置。有可能避免这种情况吗?和 .encodeSerializable(Body.serializer(), body)
在 beginStructure/endStructure 中形成类似 commonFields, bodyFields
但我需要一个平面结构`commonFields, bodyFields``。以上是关于具有多态 kotlinx 序列化的自定义序列化程序的主要内容,如果未能解决你的问题,请参考以下文章
kotlinx-serialization:找不到多态序列化器,因为缺少类鉴别器('null')