【问题标题】:Serializing Java Path with kotlinx.serialization使用 kotlinx.serialization 序列化 Java 路径
【发布时间】:2021-03-05 19:53:33
【问题描述】:

使用 kotlinx.serialization,这段代码会抛出错误: println(Json.encodeToString(Path.of("value")))kotlinx.serialization.SerializationException: Class 'WindowsPath' is not registered for polymorphic serialization in the scope of 'Path'.

WindowsPath 是内部的,因此我不能将它注册为多态子类(如this example),只能使用 Path 本身,甚至 Path 的自定义 KSerializer 也会引发相同的错误。
有什么方法可以让 Path 正确序列化/反序列化而不必将其存储为字符串?

【问题讨论】:

  • >即使是用于 Path 的自定义 KSerializer 也会引发相同的错误 您能分享自定义序列化程序的代码吗?将其显式传递给 encodeToString 应该可以工作 println(Json.encodeToString(MyCustomPathSerializer, Path.of("value")))
  • pastebin.com/Yi4c0h5R 这是一个例子。是的,直接在 encodeToString 中指定编码器是可行的,但是除了为整个文件编写自定义序列化程序之外,当路径嵌套在数据类(本示例中为“InnerObject” - 它只是不编译)时,我不确定如何解析它数据类。

标签: json kotlin serialization


【解决方案1】:

Path 是一个接口,因此它可以通过 PolymorphicSerializer 策略隐式序列化。此策略要求您为实现它的子类注册序列化程序,但如您所知,在这种情况下这是不可能的。 有一个default polymorphic serializer,但它只影响反序列化过程,并且只有在可反序列化的值为JSONObject时才有效。

对于下面的序列化器

object PathAsStringSerializer : KSerializer<Path> {
    override val descriptor = PrimitiveSerialDescriptor("Path", PrimitiveKind.STRING)

    override fun serialize(encoder: Encoder, value: Path) = encoder.encodeString(value.toAbsolutePath().toString())

    override fun deserialize(decoder: Decoder): Path = Path.of(decoder.decodeString())
}
\\Not working
val module = SerializersModule { polymorphicDefault(Path::class) { PathAsStringSerializer } }
val decoded : Path = Json { serializersModule = module }.decodeFromString("C:\\Temp")

它会抛出运行时异常kotlinx.serialization.json.internal.JsonDecodingException: Expected class kotlinx.serialization.json.JsonObject as the serialized body of kotlinx.serialization.Polymorphic&lt;Path&gt;, but had class kotlinx.serialization.json.JsonLiteral

所以,不能用普通的方式序列化,它的序列化/反序列化有3种情况,需要处理:

1。简单Path变量的序列化

在这种情况下,您需要显式传递您的自定义序列化程序:

val path = Path.of("C:\\Temp")

val message1 = Json.encodeToString(PathAsStringSerializer, path).also { println(it) }
println(Json.decodeFromString(PathAsStringSerializer, message1))

2。类的序列化,使用Path 作为泛型参数

在这种情况下,您需要定义单独的序列化程序(您可以参考原始的PathAsStringSerializer)并显式传递它们:

object ListOfPathsAsStringSerializer : KSerializer<List<Path>> by ListSerializer(PathAsStringSerializer)

val message2 = Json.encodeToString(ListOfPathsAsStringSerializer, listOf(path)).also { println(it) }
println(Json.decodeFromString(ListOfPathsAsStringSerializer, message2))
@Serializable
data class Box<T>(val item: T)
object BoxOfPathSerializer : KSerializer<Box<Path>> by Box.serializer(PathAsStringSerializer)

val message3 = Json.encodeToString(BoxOfPathSerializer, Box(path)).also { println(it) }
println(Json.decodeFromString(BoxOfPathSerializer, message3))

3。具有上述类型字段的类的序列化

在这种情况下,您需要为这些字段添加尊重的@Serializable(with = ...) 注释:

@Serializable
data class InnerObject(
    @Serializable(with = ListOfPathsAsStringSerializer::class)
    val list: MutableList<Path> = mutableListOf(),
    @Serializable(with = PathAsStringSerializer::class)
    val path: Path,
    @Serializable(with = BoxOfPathSerializer::class)
    val box: Box<Path>
)

或者只是list them once for a whole file:

@file: UseSerializers(PathAsStringSerializer::class, ListOfPathsAsStringSerializer::class, BoxOfPathSerializer::class)

这种情况下插件生成的序列化器就足够了:

val message4 = Json.encodeToString(InnerObject(mutableListOf(path), path, Box(path))).also { println(it) }
println(Json.decodeFromString<InnerObject>(message4))

【讨论】:

    猜你喜欢
    • 2021-07-22
    • 1970-01-01
    • 1970-01-01
    • 2020-05-12
    • 2021-03-09
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多