kotlin - 如何编写带有依赖项的自定义 Kotlin 序列化器?

标签 kotlin serialization dependency-injection

我有一个数据类结构,我想要序列化/反序列化。它包含一个类,我只想序列化其中的 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。因此,为什么该框架以序列化器的类定义为中心。

考虑到这一点,有两种方法可以解决向序列化器提供参数的问题:

  1. 在具有零参数构造函数的类中定义序列化器,该构造函数静态接收您的依赖项(此处为 ThingRepository);或
  2. 使用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))
}

您还需要在编译时告诉插件您想要为该类使用上下文序列化器。有多种方法可以做到这一点,具体取决于您的喜好:

  1. 注释原始类

这可行,但编译器会发出警告,因为您在特定类上使用类型 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/

    相关文章:

    Android jetpack compose NoSuchMethodError : No static method setContent

    java - 嵌套viewpagers : letting the outer swipe but not the inner

    ios - 保存自定义 Objective-c 对象层次结构以实现持久性的最佳方法

    java - 当我们允许注入(inject)时如何管理 ExecutorService 的关闭?

    node.js - NestJS - Nest 无法解析 ConfigurationService 的依赖关系(ConfigService,?)

    spring - 使用依赖注入(inject)时,如何忽略缺失的属性?

    android - 如何在Android Kotlin的RecyclerView中为CheckBox添加isChecked和unChecked事件

    android - 如何创建具有自定义 ID 值的微调器?

    jquery - 使用 JQuery SerializeArray 时合并相同命名的字段

    Python序列化词法闭包?