我有一个数据类结构,我想要序列化/反序列化。它包含一个类,我只想序列化其中的 id 而忽略其余数据,并在反序列化期间使用 id 从存储库加载其余数据。像这样的事情:
@Serializable
data class Thing(
val id: String,
val name: String
// etc.
)
class ThingSerializer(private val thingRepository: ThingRepository) : KSerializer<Thing> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Thing", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Thing) {
encoder.encodeString(value.id)
}
override fun deserialize(decoder: Decoder): Thing {
val id = decoder.decodeString()
return thingRepository.findById(id)!!
}
}
有没有办法创建此序列化器的实例并配置 SerializationModule
来使用它?
最佳答案
首先请注意,Kotlin 序列化是围绕仅在编译时定义的序列化器设计的,因为这提供了“类型安全、性能并避免反射使用”1。因此,为什么该框架以序列化器的类定义为中心。
考虑到这一点,有两种方法可以解决向序列化器提供参数的问题:
- 在具有零参数构造函数的类中定义序列化器,该构造函数静态接收您的依赖项(此处为
ThingRepository
);或 - 使用contextual serialization ,用于编译时未定义的序列化器的功能。
1。静态提供依赖
通过这种方法,您可以使用静态字段编写序列化器,即:
class ThingSerializer : KSerializer<Thing> {
companion object {
var thingRepository: ThingRepository? = null
}
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Thing", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Thing) {
encoder.encodeString(value.id)
}
override fun deserialize(decoder: Decoder): Thing {
val id = decoder.decodeString()
return thingRepository!!.findById(id)!!
}
}
您可以通过注释目标类来告诉插件有关序列化器的信息:
@Serializable(with = ThingSerializer::class)
data class Thing
您还必须在序列化之前设置静态字段:
ThingSerializer.thingRepository = thingRepository
2.上下文序列化
这种方法允许您在序列化器模块中注册序列化器实例,而不是附加一个类,如下所示:
val serializersModule = SerializersModule {
contextual(ThingSerializer(thingRepository))
}
您还需要在编译时告诉插件您想要为该类使用上下文序列化器。有多种方法可以做到这一点,具体取决于您的喜好:
- 注释原始类
这可行,但编译器会发出警告,因为您在特定类上使用类型 Any
的序列化程序。因此有必要抑制警告:
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@OptIn(ExperimentalSerializationApi::class)
@Serializable(with = ContextualSerializer::class)
data class Thing
- 使用
@Contextual
注释每个用法
如果您不想抑制警告,则可以在目标类出现时用 @Contextual
对其进行注释。当然,这样做的缺点是您必须在任何地方都包含该注释。
@Serializable
class ThingUser(@Contextual private val thing: Thing)
- 使用
@UseContextualSerialization
为每个文件添加用法注释
您还可以使用文件级注释:
@file:UseContextualSerialization(Thing::class)
package com.thing
class ThingUserTwo(private val thing: Thing)
最后,您必须记住在创建 Json
实例(或等效实例)时提供序列化器模块:
val json = Json { serializersModule = serializersModule }
1请参阅 serialization KEEP 。据推测,框架在执行代码生成时在编译时实例化序列化器。
关于kotlin - 如何编写带有依赖项的自定义 Kotlin 序列化器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77801648/