假设有一个接口和2个(或更多)实现:
interface IRunnable {
fun run()
}
class Horse : IRunnable {
override fun run() { println("horse running") }
}
class Dog : IRunnable {
override fun run() { println("dog running") }
}
我想实现用于网络传输的最简洁的JSON,即,对于Horse,{"runnable":"H"}
和对于Dog,{"runnable":"D"}
如果我无法修改接口和实现,我想将接口/实现序列化为JSON,根据Kotlin的文档,我必须编写custom serializer并使用{{ 1}}实现目标。
假设只有SerializersModule
和Horse
实现Dog
,这就是我所做的:
IRunnable
但是当我尝试将class RunnableSerializer : KSerializer<IRunnable> {
override val descriptor: SerialDescriptor
get() = StringDescriptor.withName("runnable")
override fun serialize(encoder: Encoder, obj: IRunnable) {
val stringValue = when (obj) {
is Horse -> { "H" }
is Dog -> { "D" }
else -> { null }
}
stringValue?.also {
encoder.encodeString(it)
}
}
override fun deserialize(decoder: Decoder): IRunnable {
return decoder.decodeString().let { value ->
when(value) {
"H" -> Horse()
"D" -> Dog()
else -> throw RuntimeException("invalid $value")
}
}
}
}
转换为JSON时...
Horse
它输出错误:
class RunnableTest {
private val logger = KotlinLogging.logger { }
@ImplicitReflectionSerializer
@Test
fun testJson() {
val module1 = serializersModuleOf(IRunnable::class, RunnableSerializer())
Json(context = module1).stringify(IRunnable::class.serializer(), Horse()).also {
logger.info("json = {}", it)
}
}
}
如何实现类似
在这种情况下是kotlinx.serialization.SerializationException:
Can't locate argument-less serializer for class IRunnable. For generic classes, such as lists, please provide serializer explicitly.
还是{"runnable":"H"}
?
谢谢。
环境:
{"runnable":"D"}
已更新,完整的错误消息:
<kotlin.version>1.3.60</kotlin.version>
<serialization.version>0.14.0</serialization.version>
答案 0 :(得分:3)
您要实现的目标称为多态序列化,which kotlinx.serialization supports。简而言之:它要求您将IRunnable
的所有派生类型注册为SerialModule
中的多态。对于您的代码,其外观应如下所示:
val serialModule = SerializersModule {
polymorphic(IRunnable::class) {
Horse::class with HorseSerializer
Dog::class with DogSerializer
}
}
当前,您仅注册IRunnable
,该异常正确地表明了@Serializable
无法初始化。您将如何初始化接口?你不能相反,您想知道要初始化的特定派生类型。这需要在JSON数据中嵌入类型信息,这就是多态序列化的作用。
此外,您不一定必须实现自定义序列化程序。根据您链接到的文档,您可以创建external serializers for library classes。以下内容可能就足够了,因为kotlinx.serialization编译器插件将尝试为您生成合适的序列化器,类似于您直接在类上添加@Serializer(forClass = Horse::class)
object HorseSerializer {}
@Serializer(forClass = Dog::class)
object DogSerializer {}
的情况:
json_decode