1 /*
<lambda>null2 * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 */
4 @file:Suppress("UNCHECKED_CAST")
5
6 package sample
7
8 import kotlinx.serialization.*
9 import kotlinx.serialization.builtins.*
10 import kotlinx.serialization.json.*
11 import kotlinx.serialization.modules.*
12 import kotlin.reflect.*
13 import kotlin.test.*
14
15 val jsonWithDefaults = Json { encodeDefaults = true }
16
17 class JsonTest {
18
19 private val originalData = Data("Hello")
20 private val originalString =
21 """{"s":"Hello","box":{"boxed":42},"boxes":{"desc":"boxes","boxes":[{"boxed":"foo"},{"boxed":"bar"}]},"m":{}}"""
<lambda>null22 private val nonstrict: Json = Json {
23 isLenient = true
24 ignoreUnknownKeys = true
25 allowSpecialFloatingPointValues = true
26 useArrayPolymorphism = true
27 encodeDefaults = true
28 }
29
30 @Test
testStringFormnull31 fun testStringForm() {
32 val str = jsonWithDefaults.encodeToString(Data.serializer(), originalData)
33 assertEquals(originalString, str)
34 }
35
36 @Test
testSerializeBacknull37 fun testSerializeBack() {
38 val restored = jsonWithDefaults.decodeFromString(Data.serializer(), originalString)
39 assertEquals(originalData, restored)
40 }
41
genTestDatanull42 private fun genTestData(): Holder {
43 var cnt = -1
44 fun gen(): MessageWithId {
45 cnt++
46 return MessageWithId(cnt, "Message #$cnt")
47 }
48
49 return Holder(gen(), listOf(gen(), gen()), gen(), setOf(SimpleMessage()), DoubleSimpleMessage("DoubleSimple"), gen())
50 }
51
<lambda>null52 private val testModule = SerializersModule {
53 listOf(Message::class, IMessage::class, SimpleMessage::class).forEach { clz ->
54 polymorphic(clz as KClass<Any>) {
55 subclass(SimpleMessage::class)
56 subclass(DoubleSimpleMessage::class)
57 subclass(MessageWithId::class)
58 }
59 }
60 }
61
62 @Test
testEnablesImplicitlyOnInterfacesAndAbstractClassesnull63 fun testEnablesImplicitlyOnInterfacesAndAbstractClasses() {
64 val json = Json(jsonWithDefaults) { useArrayPolymorphism = true; prettyPrint = false; serializersModule = testModule }
65 val data = genTestData()
66 assertEquals("""{"iMessage":["MessageWithId",{"id":0,"body":"Message #0"}],"iMessageList":[["MessageWithId",{"id":1,"body":"Message #1"}],""" +
67 """["MessageWithId",{"id":2,"body":"Message #2"}]],"message":["MessageWithId",{"id":3,"body":"Message #3"}],"msgSet":[["SimpleMessage",""" +
68 """{"body":"Simple"}]],"simple":["DoubleSimpleMessage",{"body":"Simple","body2":"DoubleSimple"}],"withId":{"id":4,"body":"Message #4"}}""",
69 json.encodeToString(Holder.serializer(), data)
70 )
71 }
72
73 @Test
testPolymorphicForGenericUpperBoundnull74 fun testPolymorphicForGenericUpperBound() {
75 if (Platform.name == "JS") return // Does not work with JS IR, see #1072
76 val generic = GenericMessage<Message, Any>(MessageWithId(42, "body"), "body2")
77 val serial = GenericMessage.serializer(Message.serializer(), Int.serializer() as KSerializer<Any>)
78 val json = Json {
79 useArrayPolymorphism = true
80 prettyPrint = false
81 serializersModule = testModule + SerializersModule {
82 polymorphic(Any::class) {
83 subclass(Int::class)
84 subclass(String::class)
85 }
86 }
87 }
88 val s = json.encodeToString(serial, generic)
89 assertEquals("""{"value":["MessageWithId",{"id":42,"body":"body"}],"value2":["kotlin.String","body2"]}""", s)
90 }
91
92 @Test
93 @OptIn(ExperimentalSerializationApi::class)
testDescriptornull94 fun testDescriptor() {
95 val desc = Holder.serializer().descriptor
96 assertEquals(PolymorphicSerializer(IMessage::class).descriptor, desc.getElementDescriptor(0))
97 }
98
99 @Test
canBeSerializedAsDerivednull100 fun canBeSerializedAsDerived() {
101 val derived = Derived(42)
102 val msg = jsonWithDefaults.encodeToString(Derived.serializer(), derived)
103 assertEquals("""{"publicState":"A","privateState":"B","derivedState":42,"rootState":"foo"}""", msg)
104 val d2 = jsonWithDefaults.decodeFromString(Derived.serializer(), msg)
105 assertEquals(derived, d2)
106 }
107
108 @Test
canBeSerializedAsParentnull109 fun canBeSerializedAsParent() {
110 val derived = Derived(42)
111 val msg = jsonWithDefaults.encodeToString(SerializableBase.serializer(), derived)
112 assertEquals("""{"publicState":"A","privateState":"B"}""", msg)
113 val d2 = jsonWithDefaults.decodeFromString(SerializableBase.serializer(), msg)
114 assertEquals(SerializableBase(), d2)
115 // no derivedState
116 assertFailsWith<SerializationException> { jsonWithDefaults.decodeFromString(Derived.serializer(), msg) }
117 }
118
119 @Test
testWithOpenPropertynull120 fun testWithOpenProperty() {
121 val d = Derived2("foo")
122 val msgFull = Json.encodeToString(Derived2.serializer(), d)
123 assertEquals("""{"state1":"foo","state2":"foo"}""", msgFull)
124 assertEquals("""{"state1":"foo"}""", Json.encodeToString(Base1.serializer(), d))
125 val restored = Json.decodeFromString(Derived2.serializer(), msgFull)
126 val restored2 =
127 Json.decodeFromString(Derived2.serializer(), """{"state1":"bar","state2":"foo"}""") // state1 is ignored anyway
128 assertEquals("""Derived2(state1='foo')""", restored.toString())
129 assertEquals("""Derived2(state1='foo')""", restored2.toString())
130 }
131
checkNotRegisteredMessagenull132 private fun checkNotRegisteredMessage(exception: SerializationException) {
133 val expectedText =
134 "is not found in the polymorphic scope of"
135 assertEquals(true, exception.message?.contains(expectedText))
136 }
137
138 @Test
failWithoutModulesWithCustomClassnull139 fun failWithoutModulesWithCustomClass() {
140 checkNotRegisteredMessage(
141 assertFailsWith<SerializationException>("not registered") {
142 Json.encodeToString(
143 MyPolyData.serializer(),
144 MyPolyData(mapOf("a" to IntData(42)))
145 )
146 }
147 )
148 }
149
150 @Test
testWithModulesnull151 fun testWithModules() {
152 val json = Json {
153 useArrayPolymorphism = true; serializersModule = SerializersModule { polymorphic(Any::class) { subclass(IntData::class) } } }
154 assertStringFormAndRestored(
155 expected = """{"data":{"a":["sample.IntData",{"intV":42}]}}""",
156 original = MyPolyData(mapOf("a" to IntData(42))),
157 serializer = MyPolyData.serializer(),
158 format = json
159 )
160 }
161
162 /**
163 * This test should fail because PolyDerived registered in the scope of PolyBase, not kotlin.Any
164 */
165 @Test
failWithModulesNotInAnyScopenull166 fun failWithModulesNotInAnyScope() {
167 val json = Json { serializersModule = BaseAndDerivedModule }
168 checkNotRegisteredMessage(
169 assertFailsWith<SerializationException> {
170 json.encodeToString(
171 MyPolyData.serializer(),
172 MyPolyData(mapOf("a" to PolyDerived("foo")))
173 )
174 }
175 )
176 }
177
<lambda>null178 private val baseAndDerivedModuleAtAny = SerializersModule {
179 polymorphic(Any::class) {
180 subclass(PolyDerived::class)
181 }
182 }
183
184
185 @Test
testRebindModulesnull186 fun testRebindModules() {
187 val json = Json { useArrayPolymorphism = true; serializersModule = baseAndDerivedModuleAtAny }
188 assertStringFormAndRestored(
189 expected = """{"data":{"a":["sample.PolyDerived",{"id":1,"s":"foo"}]}}""",
190 original = MyPolyData(mapOf("a" to PolyDerived("foo"))),
191 serializer = MyPolyData.serializer(),
192 format = json
193 )
194 }
195
196 /**
197 * This test should fail because PolyDerived registered in the scope of kotlin.Any, not PolyBase
198 */
199 @Test
failWithModulesNotInParticularScopenull200 fun failWithModulesNotInParticularScope() {
201 val json = Json { serializersModule = baseAndDerivedModuleAtAny }
202 checkNotRegisteredMessage(
203 assertFailsWith<SerializationException> {
204 json.encodeToString(
205 MyPolyDataWithPolyBase.serializer(),
206 MyPolyDataWithPolyBase(mapOf("a" to PolyDerived("foo")), PolyDerived("foo"))
207 )
208 }
209 )
210 }
211
212 @Test
testBindModulesnull213 fun testBindModules() {
214 val json = Json { useArrayPolymorphism = true; serializersModule = (baseAndDerivedModuleAtAny + BaseAndDerivedModule) }
215 assertStringFormAndRestored(
216 expected = """{"data":{"a":["sample.PolyDerived",{"id":1,"s":"foo"}]},"polyBase":["sample.PolyDerived",{"id":1,"s":"foo"}]}""",
217 original = MyPolyDataWithPolyBase(mapOf("a" to PolyDerived("foo")), PolyDerived("foo")),
218 serializer = MyPolyDataWithPolyBase.serializer(),
219 format = json
220 )
221 }
222
223 @Test
geoTestnull224 fun geoTest() {
225 val deser = nonstrict.decodeFromString(GeoCoordinate.serializer(), """{"latitude":1.0,"longitude":1.0}""")
226 assertEquals(GeoCoordinate(1.0, 1.0), deser)
227 }
228
229 @Test
geoTest2null230 fun geoTest2() {
231 val deser = nonstrict.decodeFromString(GeoCoordinate.serializer(), """{}""")
232 assertEquals(GeoCoordinate(0.0, 0.0), deser)
233 }
234
235 @Test
geoTestValidationnull236 fun geoTestValidation() {
237 assertFailsWith<IllegalArgumentException> {
238 nonstrict.decodeFromString(GeoCoordinate.serializer(), """{"latitude":-1.0,"longitude":1.0}""")
239 }
240 }
241 }
242
assertStringFormAndRestorednull243 inline fun <reified T : Any> assertStringFormAndRestored(
244 expected: String,
245 original: T,
246 serializer: KSerializer<T>,
247 format: Json = jsonWithDefaults,
248 printResult: Boolean = false
249 ) {
250 val string = format.encodeToString(serializer, original)
251 if (printResult) println("[Serialized form] $string")
252 assertEquals(expected, string)
253 val restored = format.decodeFromString(serializer, string)
254 if (printResult) println("[Restored form] $original")
255 assertEquals(original, restored)
256 }
257