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"}
|