Kotlin 中嵌套列表对象的多态反序列化

Polymorphic deserialization of nested list object in Kotlin

提问人:Monica 提问时间:5/4/2022 最后编辑:Monica 更新时间:5/8/2022 访问量:867

问:

尝试反序列化一个非常复杂的 json 字符串,我已经做了更简单的字符串,但我正在努力如何设置它。我还发现了一些关于简单多态反序列化的示例,但无法弄清楚如何使它适应这种更复杂的情况,其中我有不同的嵌套对象,其中一些是多态的,而另一些不是。任何帮助将不胜感激!

@Serializalbe 
data class Object1(
    @SerialName("id")
    val id: String,
    @SerialName("object2"),
    val object2: List<Object2>
)

data class Object2(
    @SerialName("name")
    val name: String,
    @SerialName("object2"),
    val object3: List<Object3>
)

@Polymorphic
interface Object3: Parcelable {

    val id: String

    /**
     * [Object3.Item1],
     * [Object3.Item2]
     */
    val type: Object3.Type

    val data: Data

    val options: List<Options>
}


@Serializable
@SerialName(type)
@Parcelize
data class Item1(
    @SerialName("_version")
    val version: String,
    @SerialName("info")
    override val info: AnotherPolyMorphicItem,
    
) : Object3 {
    override val type: Type get() = Object3.Type.Item1
}

@Serializable
@SerialName(type)
@Parcelize
data class Item2(
    @SerialName("_title")
    val tile: String,
    @SerialName("info")
    val info: AnotherPolymorphicItem,
    @SerialName("post")
    val post: String
    
) : Object3 {
    override val type: Type get() = Object3.Type.Item2
}

我为 Object3 反序列化构建了这个:

object Object3Json : SerializerContainer {
    override val serializer =
        Json {
            isLenient = false
            ignoreUnknownKeys = false
            allowSpecialFloatingPointValues = true
            useArrayPolymorphism = false
            ignoreUnknownKeys = true
            useAlternativeNames = false
            classDiscriminator = "type"
            serializersModule = SerializersModule {
                polymorphic(Object3::class) {
                    subclass(Item1::class, Item1.serializer())
                }
                polymorphic(Object3::class) {
                    subclass(Item2::class, Item2.serializer())
                }
                
            }
        }
}

我也一直在反序列化更简单的json字符串,如下所示:

object DefaultJson : SerializerContainer {
    override val serializer: Json by lazy {
        Json {
            isLenient = false
            useAlternativeNames = false
        }
    }
}

val responseObject = DefaultJson.serializer.decodeFromString(
                        ResponseObject.serializer(), payload
                    )

@Serializable
data class ResponseObject(
    @SerialName("data1")
    val data1: String,
    @SerialName("data2")
    val data2: String
    )

我的问题是如何将更简单的反序列化与多态性相结合,以及如何反序列化为对象列表,就像我想反序列化 Object1 列表一样,我已经遵循了我能找到的抛出错误的示例。

Kotlin json 反 kotlinx.serialization 多态反序列化

评论


答:

-1赞 AndrewL 5/4/2022 #1

我们的项目有一些多态序列化,但我不知道你是否认为它很复杂。我们已经用标准 Jackson 实现了所有的序列化,并仔细提示了 / 注解。@JsonTypeInfo@JsonSubTypes

这是一个代码片段 - 通过在类中仅有一个类型指示符,我们就实现了往返序列化,而无需任何自定义序列化程序。在本例中,我们有 6 种类型的 Event,它们使用 3 个具体类之一(一些细节,如接口,为简洁起见省略了所有类字段):

enum class EventType(val eventClass: KClass<out Event>) {
    CLINICIAN_MEMBER_CHANNEL_MESSAGE_TO_CLINICIAN(MemberClinicianMessageEvent::class),
    CLINICIAN_MEMBER_CHANNEL_MESSAGE_TO_MEMBER(MemberClinicianMessageEvent::class),
    MENTOR_MEMBER_CHANNEL_MESSAGE_TO_MENTOR(MentorMemberMessageEvent::class),
    MENTOR_MEMBER_CHANNEL_MESSAGE_TO_MEMBER(MentorMemberMessageEvent::class),
    CARD_ACTION_TO_MEMBER(CardActionEvent::class),
    CARD_ACTION_TO_CLINICIAN(CardActionEvent::class),
}

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.EXISTING_PROPERTY,
    property = "eventType",
    visible = true
)
@JsonSubTypes(
    JsonSubTypes.Type(name = "CLINICIAN_MEMBER_CHANNEL_MESSAGE_TO_CLINICIAN", value = MemberClinicianMessageEvent::class),
    JsonSubTypes.Type(name = "CLINICIAN_MEMBER_CHANNEL_MESSAGE_TO_MEMBER", value = MemberClinicianMessageEvent::class),
    JsonSubTypes.Type(name = "MENTOR_MEMBER_CHANNEL_MESSAGE_TO_MENTOR", value = MentorMemberMessageEvent::class),
    JsonSubTypes.Type(name = "MENTOR_MEMBER_CHANNEL_MESSAGE_TO_MEMBER", value = MentorMemberMessageEvent::class),
    JsonSubTypes.Type(name = "CARD_ACTION_TO_MEMBER", value = CardActionEvent::class),
    JsonSubTypes.Type(name = "CARD_ACTION_TO_CLINICIAN", value = CardActionEvent::class),
)


data class MemberClinicianMessageEvent(
    override val id: RULID,
    override val eventType: EventType,
    override val createdBy: SkinnyProfile,
...skip...
) : StackEvent

data class MentorMemberMessageEvent(
    override val id: RULID,
    override val eventType: EventType,
    override val createdBy: SkinnyProfile,
...skip...
) : StackEvent

data class CardActionEvent(
    override val id: RULID,
    override val eventType: EventType,
    override val createdBy: SkinnyProfile,
...skip...
) : StackEvent

eventClass在第一个枚举中,序列化不需要,但允许我们在其他地方执行类型验证。

评论

0赞 Monica 5/4/2022
所以我有一个用 kotlinx 构建的多态反序列化器,问题是多态类深深地嵌套在 JSON 中。如果该类是唯一的 json,我可以进行 poly 反序列化。然而,事实并非如此,基本上假设数据类 object1 具有 val data: Object2 然后数据类 object2 具有 val item: Object3。然后数据类 Object3 有 val 的东西:Object4。则数据类 Object4 是子类型为 1 和 2 的多态类。所以我不知道如何反序列化整个JSON字符串。如果只给我一个 Object4 的 JSON,那么我使用 kotlinx 的多态反序列化器就可以工作了。
0赞 AndrewL 5/5/2022
也许 kotlinx 有一个功能限制。暂时尝试 Jackson:您只需要这样的代码:JsonMapper.builder().addModule(KotlinModule(strictNullChecks = true)).build()
1赞 Monica 5/6/2022 #2

所以这比我想象的要简单得多。在你进入多态类之前,它返回的内容看起来有点滑稽,但所有数据都可以使用我设置的数据类结构访问。

我创建了一个列表,然后稍微调整了响应以包含它,所以我现在有:dataClassObject1

@Serializable
data class Object1List(
   @SerialName("object1")
   val object1: List<Object1>
)

然后我按如下方式对其进行反序列化,有效负载是我正在反序列化的 JSON 字符串。

val response = Object3Json.serializer.decodeFromString(Object1List.serializer(), payload)