• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.serialization.json
6 
7 import kotlinx.serialization.builtins.*
8 import kotlinx.serialization.descriptors.*
9 import kotlinx.serialization.encoding.*
10 import kotlinx.serialization.json.internal.*
11 import kotlinx.serialization.*
12 import kotlinx.serialization.modules.*
13 import kotlin.test.*
14 
15 @Suppress("UnsafeCastFromDynamic")
16 class EncodeToDynamicTest {
17     @Serializable
18     data class Data(val a: Int)
19 
20     @Serializable
21     open class DataWrapper(open val s: String, val d: Data? = Data(1)) {
22         override fun equals(other: Any?): Boolean {
23             if (this === other) return true
24             if (other == null || this::class.js != other::class.js) return false
25 
26             other as DataWrapper
27 
28             if (s != other.s) return false
29             if (d != other.d) return false
30 
31             return true
32         }
33 
34         override fun hashCode(): Int {
35             var result = s.hashCode()
36             result = 31 * result + (d?.hashCode() ?: 0)
37             return result
38         }
39     }
40 
41     @Serializable
42     data class NestedList(val a: String, val list: List<Int>)
43 
44     @Serializable
45     data class ListOfLists(val l: List<List<Data>>)
46 
47     @Serializable
48     data class MapWrapper(val m: Map<String?, Int>)
49 
50     @Serializable
51     data class ComplexMapWrapper(val m: Map<String, Data>)
52 
53     @Serializable
54     data class WithChar(val a: Char)
55 
56     @Serializable
57     data class WithLong(val l: Long)
58 
59     @Serializable
60     data class AllTypes(
61         val b: Byte,
62         val s: Short,
63         val i: Int,
64         val f: Float,
65         val d: Double,
66         val c: Char,
67         val B: Boolean,
68         val S: String
69     )
70 
71     @Serializable
72     data class EnumWrapper(val e: Color)
73 
74     @Serializable
75     sealed class Sealed {
76         @Serializable
77         data class One(val string: String) : Sealed()
78     }
79 
80     @Serializable
81     class WithJsName(@JsName("b") val a: String)
82 
83     @Serializable
84     data class WithSerialName(@SerialName("b") val a: String)
85 
86     @Serializable
87     enum class Color {
88         RED,
89         GREEN,
90 
91         @SerialName("red")
92         WITH_SERIALNAME_red
93     }
94 
95     @Serializable(MyFancyClass.Companion::class)
96     data class MyFancyClass(val value: String) {
97 
98         companion object : KSerializer<MyFancyClass> {
99 
100             override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MyFancyClass", PrimitiveKind.STRING)
101             override fun serialize(encoder: Encoder, value: MyFancyClass) {
102                 encoder.encodeString("fancy ${value.value}")
103             }
104 
105             override fun deserialize(decoder: Decoder): MyFancyClass {
106                 return MyFancyClass(decoder.decodeString().removePrefix("fancy "))
107             }
108         }
109     }
110 
111     @Test
112     fun dynamicSimpleTest() {
113         assertDynamicForm(Data(42)) { data, serialized ->
114             assertEquals(data.a, serialized.a)
115         }
116 
117         assertDynamicForm(WithChar('c')) { data, serialized ->
118             assertEquals(data.a.toString(), serialized.a)
119         }
120 
121         assertDynamicForm(AllTypes(1, 2, 3, 4.0f, 5.0, 'c', true, "string"))
122 
123 
124         assertDynamicForm(WithLong(5L))
125         assertDynamicForm(WithLong(MAX_SAFE_INTEGER.toLong()))
126         assertDynamicForm(WithLong(MAX_SAFE_INTEGER.unaryMinus().toLong()))
127     }
128 
129     @Test
130     fun wrappedObjectsTest() {
131         assertDynamicForm(DataWrapper("a string", Data(42))) { data, serialized ->
132             assertEquals(data.s, serialized.s)
133             assertNotNull(serialized.d)
134             assertEquals(data.d?.a, serialized.d.a)
135         }
136     }
137 
138     @Test
139     fun listTest() {
140         assertDynamicForm(listOf(1, 2, 3, 44), serializer = ListSerializer(Int.serializer())) { data, serialized ->
141             assertNotNull(serialized.length, "length property should exist")
142             assertEquals(data.size, serialized.length)
143 
144             for (i in data.indices) {
145                 assertEquals(data[i], serialized[i])
146             }
147         }
148     }
149 
150     @Test
151     fun arrayTest() {
152         assertDynamicForm(intArrayOf(1, 2, 3, 44), serializer = IntArraySerializer(), true) { data, serialized ->
153             assertNotNull(serialized.length, "length property should exist")
154             assertEquals(data.size, serialized.length)
155 
156             for (i in data.indices) {
157                 assertEquals(data[i], serialized[i])
158             }
159         }
160     }
161 
162     @Test
163     fun nestedListTest() {
164         assertDynamicForm(NestedList("a string", listOf(1, 2, 3, 44))) { data, serialized ->
165             assertEquals(data.a, serialized.a)
166             assertNotNull(serialized.list.length, "length property should exist")
167             assertEquals(data.list.size, serialized.list.length)
168 
169             for (i in data.list.indices) {
170                 assertEquals(data.list[i], serialized.list[i])
171             }
172         }
173 
174     }
175 
176     @Test
177     fun complexMapWrapperTest() {
178         assertDynamicForm(ComplexMapWrapper(mapOf("key1" to Data(1), "key2" to Data(2))))
179     }
180 
181     @Test
182     fun mapWrapperTest() {
183         assertDynamicForm(MapWrapper(mapOf("key1" to 1, "key2" to 2)))
184     }
185 
186     @Test
187     fun listOfListsTest() {
188         assertDynamicForm(
189             ListOfLists(
190                 listOf(
191                     listOf(Data(11), Data(12), Data(13)),
192                     listOf(Data(21), Data(22))
193                 )
194             )
195         ) { data, serialized ->
196             assertEquals(data.l.size, serialized.l.length)
197             assertEquals(data.l.first().size, serialized.l[0].length)
198         }
199     }
200 
201     @Serializable
202     data class NestedCollections(val data: Map<String, Map<String, List<Int>>>)
203 
204     @Test
205     fun nestedCollections() {
206         assertDynamicForm(
207             NestedCollections(
208                 mapOf(
209                     "one" to mapOf("oneone" to listOf(11, 12, 13), "onetwo" to listOf(1)),
210                     "two" to mapOf("twotwo" to listOf(22, 23))
211                 )
212             )
213             , serializer = NestedCollections.serializer()
214         )
215     }
216 
217     @Test
218     fun enums() {
219         assertDynamicForm(EnumWrapper(Color.RED))
220         assertDynamicForm(Color.GREEN)
221         assertDynamicForm(Color.WITH_SERIALNAME_red) { _, serialized ->
222             assertEquals("red", serialized)
223         }
224     }
225 
226     @Test
227     fun singlePrimitiveValue() {
228         assertDynamicForm("some string")
229         assertDynamicForm(1.toByte())
230         assertDynamicForm(1.toShort())
231         assertDynamicForm(1)
232         assertDynamicForm(1.toFloat())
233         assertDynamicForm(1.toDouble())
234         assertDynamicForm('c')
235         assertDynamicForm(true)
236         assertDynamicForm(false)
237         assertDynamicForm(1L)
238         val result = Json.encodeToDynamic(String.serializer().nullable, null)
239         assertEquals(null, result)
240     }
241 
242     @Test
243     fun sealed() {
244         // test of sealed class but not polymorphic serialization
245         assertDynamicForm(Sealed.One("one"))
246     }
247 
248     @Test
249     fun withSerialNam() {
250         assertDynamicForm(WithSerialName("something")) { data, serialized ->
251             assertEquals(data.a, serialized.b)
252         }
253     }
254 
255     @Test
256     fun mapWithNullKey() {
257         val serialized = Json.encodeToDynamic(
258             MapSerializer(String.serializer().nullable, Int.serializer()),
259             mapOf(null to 0, "a" to 1)
260         )
261         assertNotNull(serialized[null], "null key should be present in output")
262     }
263 
264     @Test
265     fun mapWithSimpleKey() {
266 
267         inline fun <reified T> assertSimpleMapForm(key: T, value: String) {
268             assertDynamicForm(mapOf(key to value), MapSerializer(serializer(), String.serializer()))
269         }
270 
271         assertSimpleMapForm(1, "Int 1")
272         assertSimpleMapForm("s", "String s")
273         assertSimpleMapForm('c', "char c")
274         assertSimpleMapForm(2.toByte(), "Byte 2")
275         assertSimpleMapForm(3.toShort(), "Short 3")
276         assertSimpleMapForm(4.toLong(), "Long 4")
277         assertSimpleMapForm(5.toFloat(), "Float 5")
278         assertSimpleMapForm(6.toDouble(), "Double 6")
279 
280         assertDynamicForm(
281             mapOf(
282                 Color.RED to "RED",
283                 Color.GREEN to "GREEN",
284                 Color.WITH_SERIALNAME_red to "red"
285             ),
286             MapSerializer(Color.serializer(), String.serializer())
287         ) { _, serialized ->
288             assertNotNull(serialized["red"], "WITH_SERIALNAME_red should be serialized as 'red'")
289         }
290     }
291 
292     @Test
293     fun mapWithIllegalKey() {
294 
295         val exception = assertFails {
296             Json.encodeToDynamic(
297                 MapSerializer(Data.serializer(), String.serializer()),
298                 mapOf(
299                     Data(1) to "data",
300                     Data(2) to "data",
301                     Data(3) to "data"
302                 )
303             )
304         }
305         assertEquals(IllegalArgumentException::class, exception::class)
306         assertTrue("should have a helpful error message") {
307             exception.message?.contains("can't be used in json as map key") == true
308         }
309 
310         assertFails {
311             @Suppress("CAST_NEVER_SUCCEEDS")
312             assertDynamicForm(
313                 mapOf(
314                     (null as? Data) to "Data null"
315                 ),
316                 MapSerializer(Data.serializer().nullable, String.serializer())
317             )
318         }
319 
320 
321         val doubleSerializer = MapSerializer(Double.serializer(), String.serializer())
322         val value = mapOf(0.5 to "0.5")
323         var ex = assertFails {
324             assertDynamicForm(value, doubleSerializer)
325         }
326         assertTrue("should have a helpful error message") {
327             ex.message?.contains("can't be used in json as map key") == true
328         }
329 
330         ex = assertFails {
331             assertDynamicForm(mapOf(Double.NaN to "NaN"), doubleSerializer)
332         }
333         assertTrue("should have a helpful error message") {
334             ex.message?.contains("can't be used in json as map key") == true
335         }
336 
337         ex = assertFails {
338             assertDynamicForm(mapOf(Double.NEGATIVE_INFINITY to "NaN"), doubleSerializer)
339         }
340         assertTrue("should have a helpful error message") {
341             ex.message?.contains("can't be used in json as map key") == true
342         }
343 
344         assertDynamicForm(mapOf(11.0 to "11"), doubleSerializer)
345 
346     }
347 
348     @Test
349     fun customSerializerTest() {
350         assertDynamicForm(MyFancyClass("apple"), MyFancyClass.serializer()) { _, serialized ->
351             assertEquals("fancy apple", serialized)
352         }
353 
354         assertDynamicForm(
355             mapOf(MyFancyClass("apple") to "value"),
356             MapSerializer(MyFancyClass.serializer(), String.serializer())
357         ) { _, serialized ->
358             assertNotNull(serialized["fancy apple"], "should contain key 'fancy apple'")
359         }
360     }
361 }
362 
assertDynamicFormnull363 public inline fun <reified T : Any> assertDynamicForm(
364     data: T,
365     serializer: KSerializer<T> = EmptySerializersModule().serializer(),
366     skipEqualsCheck: Boolean = false,
367     noinline assertions: ((T, dynamic) -> Unit)? = null
368 ) {
369     val serialized = Json.encodeToDynamic(serializer, data)
370     assertions?.invoke(data, serialized)
371     val string = Json.encodeToString(serializer, data)
372     assertEquals(
373         string,
374         JSON.stringify(serialized),
375         "JSON.stringify representation must be the same"
376     )
377 
378     if (skipEqualsCheck) return // arrays etc.
379     assertEquals(data, Json.decodeFromString(serializer, string))
380 }
381