我有一个想要序列化/反序列化的数据类 struct .它包含一个类,我只想序列化其中的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. Providing the dependency statically

使用这种方法,您可以使用静态字段来编写序列化器,即:

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. Contextual serialization

这种方法允许您在序列化器模块中注册一个序列化器实例,而不是附加一个类,如下所示:

val serializersModule = SerializersModule {
    contextual(ThingSerializer(thingRepository))
}

您还需要在编译时告诉插件您想要为该类使用上下文序列化程序.有几种方法可以做到这一点,具体取决于你的品味:

  1. 注释原始类

这是可行的,但编译器会发出警告,因为您正在对特定类使用类型Any的序列化程序.因此,有必要取消该警告:

@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@OptIn(ExperimentalSerializationApi::class)
@Serializable(with = ContextualSerializer::class)
data class Thing
  1. 为每种用法加@Contextual个注解

如果您不希望隐藏警告,则可以在每次出现目标类时使用@Contextual对其进行注释.当然,这样做的缺点是您必须在任何地方都包含该注释.

@Serializable
class ThingUser(@Contextual private val thing: Thing)
  1. @UseContextualSerialization注释每个文件的用法

您还可以使用文件级注释:

@file:UseContextualSerialization(Thing::class)

package com.thing

class ThingUserTwo(private val thing: Thing)

最后,您必须记住在创建Json个实例(或等效实例)时提供序列化程序模块:

val json = Json { serializersModule = serializersModule }

1请参阅serialization KEEP.大概框架在执行代码生成时在编译时实例化序列化程序.

Kotlin相关问答推荐

外键是主键的一部分,但不是索引的一部分.房间

查看流数据和改进的HTTP请求的模型

处理合成层次 struct 中的深层按钮以切换视图

合并状态流

Java/Kotlin中类似Rust般的注释编译?

在kotlin中匹配多个变量

Kotlin:调用 CoroutineScope.launch 与在协程内启动之间的区别

按钮无法在 Android Studio 上打开新活动

如何使用 Mockk 模拟返回值类的 Kotlin 函数类型?

为什么 IntelliJ Idea 无法识别我的 Spek 测试?

在 kotlin 中写入 parcer 可空值

IntelliJ 不会根据 ktlint 的期望对 Kotlin 导入进行排序

在 Spring Framework 5.1 中注册具有相同名称的测试 bean

接口中的属性不能有支持字段

android Room 将 Null 作为非 Null 类型返回

IllegalStateException:function = , count = 3, index = 3

在 kotlin 中,如何将主构造函数中的属性设置器设为私有?

Failure delivering result on activity result

WebFlux 功能:如何检测空 Flux 并返回 404?

lateinit 的 isInitialized 属性在伴随对象中不起作用