kotlinx 序列化 — 进行多态子反序列化的最佳方法

int*_*_32 3 json kotlin kotlinx.serialization kotlinx

我有一个 Json 输入,例如:

\n
{\n   "type": "type_1",\n   "data": {\n      // ...\n   }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

data字段可能会有所不同,具体取决于type

\n

所以,我需要一个反序列化器,它分别查看type(enum) 和反序列化data(例如,对于type_1值它是Type1类,对于type_2\xe2\x80\x94Type2等)。

\n

我考虑过一个完全自定义的反序列化器(扩展 a KSerializer<T>),但它看起来有点矫枉过正。

\n

进行此类反序列化的最佳(kotlin)方法是什么?

\n

Мих*_*аль 7

Kotlin 多态反序列化的方式是使用纯 JSON(所有data字段与字段处于同一级别type):

{
   "type": "type_1",
   // ...
}
Run Code Online (Sandbox Code Playgroud)

并使用序列化器模块注册抽象超类的所有子类(如果超类是一个类,则可以跳过此步骤sealed)。

不需要枚举 -@SerialName("type_1")如果 JSON 中的名称与完全限定的类名称不同,只需使用尊重的注释标记子类声明即可。

如果原始 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)
}
Run Code Online (Sandbox Code Playgroud)