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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
/*
* Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.serialization.features
import kotlinx.serialization.*
import kotlinx.serialization.builtins.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.*
import kotlinx.serialization.test.*
import kotlin.test.*
class ContextAndPolymorphicTest {
@Serializable
data class Data(val a: Int, val b: Int = 42)
@Serializable
data class EnhancedData(
val data: Data,
@Contextual val stringPayload: Payload,
@Serializable(with = BinaryPayloadSerializer::class) val binaryPayload: Payload
)
@Serializable
@SerialName("Payload")
data class Payload(val s: String)
@Serializable
data class PayloadList(val ps: List<@Contextual Payload>)
@Serializer(forClass = Payload::class)
object PayloadSerializer
object BinaryPayloadSerializer : KSerializer<Payload> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BinaryPayload", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Payload) {
encoder.encodeString(InternalHexConverter.printHexBinary(value.s.encodeToByteArray()))
}
override fun deserialize(decoder: Decoder): Payload {
return Payload(InternalHexConverter.parseHexBinary(decoder.decodeString()).decodeToString())
}
}
private val value = EnhancedData(Data(100500), Payload("string"), Payload("binary"))
private lateinit var json: Json
@BeforeTest
fun initContext() {
val scope = serializersModuleOf(Payload::class, PayloadSerializer)
val bPolymorphicModule = SerializersModule { polymorphic(Any::class) { subclass(PayloadSerializer) } }
json = Json {
useArrayPolymorphism = true
encodeDefaults = true
serializersModule = scope + bPolymorphicModule
}
}
@Test
fun testWriteCustom() {
val s = json.encodeToString(EnhancedData.serializer(), value)
assertEquals("""{"data":{"a":100500,"b":42},"stringPayload":{"s":"string"},"binaryPayload":"62696E617279"}""", s)
}
@Test
fun testReadCustom() {
val s = json.decodeFromString(EnhancedData.serializer(),
"""{"data":{"a":100500,"b":42},"stringPayload":{"s":"string"},"binaryPayload":"62696E617279"}""")
assertEquals(value, s)
}
@Test
fun testWriteCustomList() {
val s = json.encodeToString(PayloadList.serializer(), PayloadList(listOf(Payload("1"), Payload("2"))))
assertEquals("""{"ps":[{"s":"1"},{"s":"2"}]}""", s)
}
@Test
fun testPolymorphicResolve() {
val map = mapOf<String, Any>("Payload" to Payload("data"))
val serializer = MapSerializer(String.serializer(), PolymorphicSerializer(Any::class))
val s = json.encodeToString(serializer, map)
assertEquals("""{"Payload":["Payload",{"s":"data"}]}""", s)
}
@Test
fun testDifferentRepresentations() {
val simpleModule = serializersModuleOf(PayloadSerializer)
val binaryModule = serializersModuleOf(BinaryPayloadSerializer)
val json1 = Json { useArrayPolymorphism = true; serializersModule = simpleModule }
val json2 = Json { useArrayPolymorphism = true; serializersModule = binaryModule }
// in json1, Payload would be serialized with PayloadSerializer,
// in json2, Payload would be serialized with BinaryPayloadSerializer
val list = PayloadList(listOf(Payload("string")))
assertEquals("""{"ps":[{"s":"string"}]}""", json1.encodeToString(PayloadList.serializer(), list))
assertEquals("""{"ps":["737472696E67"]}""", json2.encodeToString(PayloadList.serializer(), list))
}
private fun SerialDescriptor.inContext(module: SerializersModule): SerialDescriptor = when (kind) {
SerialKind.CONTEXTUAL -> requireNotNull(module.getContextualDescriptor(this)) { "Expected $this to be registered in module" }
else -> error("Expected this function to be called on CONTEXTUAL descriptor")
}
@Test
fun testResolveContextualDescriptor() {
val simpleModule = serializersModuleOf(PayloadSerializer)
val binaryModule = serializersModuleOf(BinaryPayloadSerializer)
val contextDesc = EnhancedData.serializer().descriptor.elementDescriptors.toList()[1] // @ContextualSer stringPayload
assertEquals(SerialKind.CONTEXTUAL, contextDesc.kind)
assertEquals(0, contextDesc.elementsCount)
val resolvedToDefault = contextDesc.inContext(simpleModule)
assertEquals(StructureKind.CLASS, resolvedToDefault.kind)
assertEquals("Payload", resolvedToDefault.serialName)
assertEquals(1, resolvedToDefault.elementsCount)
val resolvedToBinary = contextDesc.inContext(binaryModule)
assertEquals(PrimitiveKind.STRING, resolvedToBinary.kind)
assertEquals("BinaryPayload", resolvedToBinary.serialName)
}
@Test
fun testContextualSerializerUsesDefaultIfModuleIsEmpty() {
val s = Json { useArrayPolymorphism = true; encodeDefaults = true }.encodeToString(EnhancedData.serializer(), value)
assertEquals("""{"data":{"a":100500,"b":42},"stringPayload":{"s":"string"},"binaryPayload":"62696E617279"}""", s)
}
}
|