avatar CyanTachyon's Blog

CyanTachyon的个人博客

  • 首页
  • 分类
  • 标签
  • 归档
  • 关于
首页 Kotlin序列化多态
文章

Kotlin序列化多态

发表于 2024/11/25 更新于 2025/04/12
作者 CyanTachyon
8 分钟阅读
Kotlin序列化多态
Kotlin序列化多态

Kotlin序列化多态

关于Kt多态简述

当在序列化中出现多态时,会在序列化结果中带上类判别器(classDiscriminator),以区分不同的class。默认为type,并且可以通过classDiscriminator修改。也可以通过classDiscriminatorMode修改何时添加判别器

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@file:OptIn(ExperimentalSerializationApi::class)  
  
import kotlinx.serialization.ExperimentalSerializationApi  
import kotlinx.serialization.Serializable  
import kotlinx.serialization.encodeToString  
import kotlinx.serialization.json.ClassDiscriminatorMode  
import kotlinx.serialization.json.Json  
  
@Serializable  
sealed interface A  
  
@Serializable  
data class B(val some: String = "B"): A  
  
@Serializable  
data class C(val some: String = "C"): A  
  
fun main()  
{  
    println(Json.encodeToString(B("b") as A))  
    var json = Json()  
    {  
        classDiscriminator = "custom_class_discriminator"  
    }  
    println(json.encodeToString(C("c") as A))  
    json = Json(json)  
    {  
        classDiscriminatorMode = ClassDiscriminatorMode.NONE  
    }  
    println(json.encodeToString(C("c") as A))  
}
0
1
2
{"type":"B","some":"b"}
{"custom_class_discriminator":"C","some":"c"}
{"some":"c"}

不同类的类判别器特殊对待

但有时我们会希望使用一个json对象对于不同的类时而带有类判别器,时而不带(例如在ktor项目中的内容协商,整个项目不得不使用同一个json对象进行序列化)。 例如:由于A有反序列需求,我们希望A带有类判别器,而B不带。

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import kotlinx.serialization.Serializable  
import kotlinx.serialization.encodeToString  
import kotlinx.serialization.json.Json  
  
@Serializable  
sealed interface A  
  
@Serializable  
data class AB(val some: String = "B"): A  
  
@Serializable  
data class AC(val some: String = "C"): A  
  
@Serializable  
sealed interface B  
@Serializable  
data class BB(val some: String = "B"): B  
  
@Serializable  
data class BC(val some: String = "C"): B  
  
fun main()  
{  
    val json = Json()  
    {  
        // TODO  
    }  
	println(json.encodeToString(AB() as A)) // 期望: {type: "AB", some: "B"}
    println(json.encodeToString(AC() as A)) // 期望: {type: "AC", some: "C"}
    println(json.encodeToString(BB() as B)) // 期望: {some: "B"}
    println(json.encodeToString(BC() as B)) // 期望: {some: "C"}
}

方法1

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import kotlinx.serialization.*  
import kotlinx.serialization.descriptors.PolymorphicKind  
import kotlinx.serialization.descriptors.SerialDescriptor  
import kotlinx.serialization.descriptors.buildSerialDescriptor  
import kotlinx.serialization.encoding.Decoder  
import kotlinx.serialization.encoding.Encoder  
import kotlinx.serialization.json.*  
  
@OptIn(ExperimentalSerializationApi::class)  
@Serializable(A.ASerializer::class)  
sealed interface A  
{  
    object ASerializer: KSerializer<A>  
    {  
        @OptIn(InternalSerializationApi::class)  
        override val descriptor: SerialDescriptor =  
            buildSerialDescriptor("A", PolymorphicKind.SEALED)  
            {  
                element("AB", AB.serializer().descriptor)  
                element("AC", AC.serializer().descriptor)  
            }  
  
        override fun serialize(encoder: Encoder, value: A)  
        {  
            when(value)  
            {  
                is AB -> encoder.encodeSerializableValue(AB.serializer(), value)  
                is AC -> encoder.encodeSerializableValue(AC.serializer(), value)  
            }  
        }  
  
        override fun deserialize(decoder: Decoder): A  
        {  
            val element = decoder.decodeSerializableValue(JsonElement.serializer())  
            val some = element.jsonObject["some"]?.jsonPrimitive  
            return if (some?.content == "B" && some.isString == true) AB() else AC()  
        }  
    }  
}  
@Serializable  
data class AB(val some: String = "B"): A  
@Serializable  
data class AC(val some: String = "C"): A  
  
@Serializable  
sealed interface B  
@Serializable  
data class BB(val some: String = "B"): B  
@Serializable  
data class BC(val some: String = "C"): B  
  
@OptIn(ExperimentalSerializationApi::class)  
fun main()  
{  
    val json = Json()  
    {  
        encodeDefaults = true  
        classDiscriminatorMode = ClassDiscriminatorMode.NONE  
    }  
  
    println(json.encodeToString(AB() as A)) // 期望: {type: "AB", some: "B"}   
    println(json.encodeToString(AC() as A)) // 期望: {type: "AC", some: "C"}   
    println(json.encodeToString(BB() as B)) // 期望: {some: "B"}   
    println(json.encodeToString(BC() as B)) // 期望: {some: "C"}  
    println(json.decodeFromString<A>(json.encodeToString(AB() as A)))  
    runCatching {  
        println(json.decodeFromString<B>(json.encodeToString(BB() as B)))  
    }.onFailure {  
        println(it)  
    }  
}
0
1
2
3
4
5
6
{"some":"B"}
{"some":"C"}
{"some":"B"}
{"some":"C"}
AB(some=B)
kotlinx.serialization.json.internal.JsonDecodingException: Class discriminator was missing and no default serializers were registered in the polymorphic scope of 'B'.
JSON input: {"some":"B"}

最简单直接的解决方案,自己实现KSerializer解决几乎所有问题

方法2

关闭类判别器,并手动添加type类型

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import kotlinx.serialization.ExperimentalSerializationApi  
import kotlinx.serialization.Serializable  
import kotlinx.serialization.encodeToString  
import kotlinx.serialization.json.ClassDiscriminatorMode  
import kotlinx.serialization.json.Json  
  
@Serializable  
sealed interface A  
  
@Serializable  
data class AB(val some: String = "B"): A  
{  
    val type = "AB"  
}  
  
@Serializable  
data class AC(val some: String = "C"): A  
{  
    val type = "AC"  
}  
  
@Serializable  
sealed interface B  
@Serializable  
data class BB(val some: String = "B"): B  
  
@Serializable  
data class BC(val some: String = "C"): B  
  
@OptIn(ExperimentalSerializationApi::class)  
fun main()  
{  
    val json = Json()  
    {  
        encodeDefaults = true  
        classDiscriminatorMode = ClassDiscriminatorMode.NONE  
    }  
    println(json.encodeToString(AB() as A)) // 期望: {type: "AB", some: "B"}
    println(json.encodeToString(AC() as A)) // 期望: {type: "AC", some: "C"}   
    println(json.encodeToString(BB() as B)) // 期望: {some: "B"}   
    println(json.encodeToString(BC() as B)) // 期望: {some: "C"}  
    println(json.decodeFromString<A>(json.encodeToString(AB() as A)))  
    runCatching {  
        println(json.decodeFromString<B>(json.encodeToString(BB() as B)))  
    }.onFailure {  
        println(it)  
    }  
}
0
1
2
3
4
5
6
{"some":"B","type":"AB"}
{"some":"C","type":"AC"}
{"some":"B"}
{"some":"C"}
AB(some=B)
kotlinx.serialization.json.internal.JsonDecodingException: Class discriminator was missing and no default serializers were registered in the polymorphic scope of 'B'.
JSON input: {"some":"B"}

可以看到,输出与期望相符,并且A类也可以正确反序列化。 并且通过这种方式还可以使反序列类型和序列化的类型不同

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import kotlinx.serialization.ExperimentalSerializationApi  
import kotlinx.serialization.Serializable  
import kotlinx.serialization.encodeToString  
import kotlinx.serialization.json.ClassDiscriminatorMode  
import kotlinx.serialization.json.Json

@Serializable
sealed interface A
@Serializable
data class AB(val some: String = "B"): A
{  
    val type = "AC"  
}
@Serializable
data class AC(val some: String = "C"): A
{  
    val type = "AC"  
}

@Serializable
sealed interface B  
@Serializable
data class BB(val some: String = "B"): B
@Serializable
data class BC(val some: String = "C"): B
  
@OptIn(ExperimentalSerializationApi::class)  
fun main()  
{  
    val json = Json()  
    {  
        encodeDefaults = true  
        classDiscriminatorMode = ClassDiscriminatorMode.NONE  
    }  
    println(json.encodeToString(AB() as A)) // 期望: {type: "AB", some: "B"}    
    println(json.encodeToString(AC() as A)) // 期望: {type: "AC", some: "C"}    
    println(json.encodeToString(BB() as B)) // 期望: {some: "B"}    
    println(json.encodeToString(BC() as B)) // 期望: {some: "C"}  
    println(json.decodeFromString<A>(json.encodeToString(AB() as A)))  
    runCatching {  
        println(json.decodeFromString<B>(json.encodeToString(BB() as B)))  
    }.onFailure {  
        println(it)  
    }  
}
0
1
2
3
4
5
6
{"some":"B","type":"AC"}
{"some":"C","type":"AC"}
{"some":"B"}
{"some":"C"}
AC(some=B)
kotlinx.serialization.json.internal.JsonDecodingException: Class discriminator was missing and no default serializers were registered in the polymorphic scope of 'B'.
JSON input: {"some":"B"}

同时,也可以与JsonClassDiscriminator配合

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import kotlinx.serialization.ExperimentalSerializationApi  
import kotlinx.serialization.Serializable  
import kotlinx.serialization.encodeToString  
import kotlinx.serialization.json.ClassDiscriminatorMode  
import kotlinx.serialization.json.Json  
import kotlinx.serialization.json.JsonClassDiscriminator  
  
@Serializable  
@JsonClassDiscriminator("custom")  
sealed interface A  
@Serializable  
data class AB(val some: String = "B"): A  
{  
    val custom = "AB"  
}  
@Serializable  
data class AC(val some: String = "C"): A  
{  
    val custom = "AC"  
}  
  
@Serializable  
sealed interface B  
@Serializable  
data class BB(val some: String = "B"): B  
@Serializable  
data class BC(val some: String = "C"): B  
  
@OptIn(ExperimentalSerializationApi::class)  
fun main()  
{  
    val json = Json()  
    {  
        encodeDefaults = true  
        classDiscriminatorMode = ClassDiscriminatorMode.NONE  
    }  
    println(json.encodeToString(AB() as A)) // 期望: {type: "AB", some: "B"}
    println(json.encodeToString(AC() as A)) // 期望: {type: "AC", some: "C"}   
    println(json.encodeToString(BB() as B)) // 期望: {some: "B"}   
    println(json.encodeToString(BC() as B)) // 期望: {some: "C"}  
    println(json.decodeFromString<A>(json.encodeToString(AB() as A)))  
    runCatching {  
        println(json.decodeFromString<B>(json.encodeToString(BB() as B)))  
    }.onFailure {  
        println(it)  
    }  
}
0
1
2
3
4
5
6
{"some":"B","custom":"AB"}
{"some":"C","custom":"AC"}
{"some":"B"}
{"some":"C"}
AB(some=B)
kotlinx.serialization.json.internal.JsonDecodingException: Class discriminator was missing and no default serializers were registered in the polymorphic scope of 'B'.
JSON input: {"some":"B"}
develop, kotlin
kotlin develop
本文由作者按照 CC BY-NC 4.0 进行授权, 未经许可不得转载。
分享

最近更新

  • SubQuiz Docs
  • Hash&KMP
  • c++快读快写
  • Java快速入门指南
  • Kotlin序列化多态

热门标签

develop kotlin cpp工具 java project vue 信息作业

外部链接

  •  此博客的 Github 仓库
  •  此博客的评论仓库

文章内容

相关文章

2024/11/28

FuckBrainfuck

FuckBrainfuck 先叠甲 以下内容纯属娱乐,不具有任何实际意义。我无法保证我的实现没有问题,如有问题欢迎指出。 Brainfuck简述 若你已经知道何为Brainfuck,请跳过此部分. Brainfuck是一种极简单的编程语言,由Urban Müller于1993年创造。它只包含八个命令,分别是&gt; &lt; + - [ ] . ,。它的内存模型是一个无限长度的字节数...

2025/04/13

SubQuiz Docs

概述 SubQuiz原为SubIT社团开发的一款智能答题系统,之后该系统将移交北大附中科协,作为北大附中学生刷题练习网站,成为北大附中信息化的一部分。 功能 1.0 题目 题目结构及分类: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |- 学科1 | |- 学科描述 | |- 题目类型1 | ...

2024/03/01

Java快速入门指南

Java快速入门指南 本文旨在帮助学习过其他语言(尤其是面向对象语言)如C++、Rust的人快速上手Java。 如有错误还请指正 简单语法介绍 声明函数/变量时和C/C++一样类型前置 对于类名和包名需要使用大驼峰(首字母大写的驼峰)命名,变量/函数需要使用小驼峰命名 java是一个面向对象的语言,也就是有”类”这一概念。 数据类型 java中的类型分为基本数据类...

Java快速入门指南

FuckBrainfuck

© 2025 CyanTachyon. 保留部分权利。

|

本站采用 Jekyll 主题 Chirpy

热门标签

develop kotlin cpp工具 java project vue 信息作业

发现新版本的内容。