kotlinx 序列化——进行多态子反序列化的最佳方式
kotlinx serialization — best way to do polymorphic child deserialization
我有一个 Json 输入,例如:
{
"type": "type_1",
"data": {
// ...
}
}
data
字段可能因 type
.
而异
所以,我需要一个反序列化器,它查看 type
(枚举)并分别反序列化 data
(例如,对于 type_1
值,它是 Type1
class,对于 type_2
— Type2
,等等)。
我考虑过一个完全自定义的反序列化器(扩展 KSerializer<T>
),但它看起来有点矫枉过正。
进行这种反序列化的最佳 (kotlin) 方法是什么?
用于多态反序列化的 Kotlin 方法是使用普通 JSON(所有 data
字段与 type
字段处于同一级别):
{
"type": "type_1",
// ...
}
并用 serializers module 注册抽象 superclass 的所有子class(如果 superclass 是 sealed
[,则可以跳过此步骤) =25=]).
不需要枚举 - 如果它在 JSON 中的名称与完全限定的 class 名称不同,只需用尊重的 @SerialName("type_1")
注释标记 subclasses 声明。
如果原始 JSON 形状是一个严格的要求,那么您可以将其动态转换为普通形状,将任务减少到前一个。
@Serializable(with = CommonAbstractSuperClassDeserializer::class)
abstract class CommonAbstractSuperClass
@Serializable
@SerialName("type_1")
data class Type1(val x: Int, val y: Int) : CommonAbstractSuperClass()
@Serializable
@SerialName("type_2")
data class Type2(val a: String, val b: Type1) : CommonAbstractSuperClass()
object CommonAbstractSuperClassDeserializer :
JsonTransformingSerializer<CommonAbstractSuperClass>(PolymorphicSerializer(CommonAbstractSuperClass::class)) {
override fun transformDeserialize(element: JsonElement): JsonElement {
val type = element.jsonObject["type"]!!
val data = element.jsonObject["data"] ?: return element
return JsonObject(data.jsonObject.toMutableMap().also { it["type"] = type })
}
}
fun main() {
val kotlinx = Json {
serializersModule = SerializersModule {
polymorphic(CommonAbstractSuperClass::class) {
subclass(Type1::class)
subclass(Type2::class)
}
}
}
val str1 = "{\"type\":\"type_1\",\"data\":{\"x\":1,\"y\":1}}"
val obj1 = kotlinx.decodeFromString<CommonAbstractSuperClass>(str1)
println(obj1) //Type1(x=1, y=1)
val str2 = "{\"type\":\"type_2\",\"data\":{\"a\":\"1\",\"b\":{\"x\":1,\"y\":1}}}"
val obj2 = kotlinx.decodeFromString<CommonAbstractSuperClass>(str2)
println(obj2) //Type2(a=1, b=Type1(x=1, y=1))
//Works for plain JSON shape as well:
val str0 = "{\"type\":\"type_1\",\"x\":1,\"y\":1}"
val obj0 = kotlinx.decodeFromString<CommonAbstractSuperClass>(str0)
println(obj0) //Type1(x=1, y=1)
}
我有一个 Json 输入,例如:
{
"type": "type_1",
"data": {
// ...
}
}
data
字段可能因 type
.
所以,我需要一个反序列化器,它查看 type
(枚举)并分别反序列化 data
(例如,对于 type_1
值,它是 Type1
class,对于 type_2
— Type2
,等等)。
我考虑过一个完全自定义的反序列化器(扩展 KSerializer<T>
),但它看起来有点矫枉过正。
进行这种反序列化的最佳 (kotlin) 方法是什么?
用于多态反序列化的 Kotlin 方法是使用普通 JSON(所有 data
字段与 type
字段处于同一级别):
{
"type": "type_1",
// ...
}
并用 serializers module 注册抽象 superclass 的所有子class(如果 superclass 是 sealed
[,则可以跳过此步骤) =25=]).
不需要枚举 - 如果它在 JSON 中的名称与完全限定的 class 名称不同,只需用尊重的 @SerialName("type_1")
注释标记 subclasses 声明。
如果原始 JSON 形状是一个严格的要求,那么您可以将其动态转换为普通形状,将任务减少到前一个。
@Serializable(with = CommonAbstractSuperClassDeserializer::class)
abstract class CommonAbstractSuperClass
@Serializable
@SerialName("type_1")
data class Type1(val x: Int, val y: Int) : CommonAbstractSuperClass()
@Serializable
@SerialName("type_2")
data class Type2(val a: String, val b: Type1) : CommonAbstractSuperClass()
object CommonAbstractSuperClassDeserializer :
JsonTransformingSerializer<CommonAbstractSuperClass>(PolymorphicSerializer(CommonAbstractSuperClass::class)) {
override fun transformDeserialize(element: JsonElement): JsonElement {
val type = element.jsonObject["type"]!!
val data = element.jsonObject["data"] ?: return element
return JsonObject(data.jsonObject.toMutableMap().also { it["type"] = type })
}
}
fun main() {
val kotlinx = Json {
serializersModule = SerializersModule {
polymorphic(CommonAbstractSuperClass::class) {
subclass(Type1::class)
subclass(Type2::class)
}
}
}
val str1 = "{\"type\":\"type_1\",\"data\":{\"x\":1,\"y\":1}}"
val obj1 = kotlinx.decodeFromString<CommonAbstractSuperClass>(str1)
println(obj1) //Type1(x=1, y=1)
val str2 = "{\"type\":\"type_2\",\"data\":{\"a\":\"1\",\"b\":{\"x\":1,\"y\":1}}}"
val obj2 = kotlinx.decodeFromString<CommonAbstractSuperClass>(str2)
println(obj2) //Type2(a=1, b=Type1(x=1, y=1))
//Works for plain JSON shape as well:
val str0 = "{\"type\":\"type_1\",\"x\":1,\"y\":1}"
val obj0 = kotlinx.decodeFromString<CommonAbstractSuperClass>(str0)
println(obj0) //Type1(x=1, y=1)
}