summaryrefslogtreecommitdiff
path: root/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt
diff options
context:
space:
mode:
Diffstat (limited to 'formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt')
-rw-r--r--formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt380
1 files changed, 380 insertions, 0 deletions
diff --git a/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt b/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt
new file mode 100644
index 00000000..74196b7b
--- /dev/null
+++ b/formats/json-tests/jsTest/src/kotlinx/serialization/json/EncodeToDynamicTest.kt
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+@Suppress("UnsafeCastFromDynamic")
+class EncodeToDynamicTest {
+ @Serializable
+ data class Data(val a: Int)
+
+ @Serializable
+ open class DataWrapper(open val s: String, val d: Data? = Data(1)) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class.js != other::class.js) return false
+
+ other as DataWrapper
+
+ if (s != other.s) return false
+ if (d != other.d) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = s.hashCode()
+ result = 31 * result + (d?.hashCode() ?: 0)
+ return result
+ }
+ }
+
+ @Serializable
+ data class NestedList(val a: String, val list: List<Int>)
+
+ @Serializable
+ data class ListOfLists(val l: List<List<Data>>)
+
+ @Serializable
+ data class MapWrapper(val m: Map<String?, Int>)
+
+ @Serializable
+ data class ComplexMapWrapper(val m: Map<String, Data>)
+
+ @Serializable
+ data class WithChar(val a: Char)
+
+ @Serializable
+ data class WithLong(val l: Long)
+
+ @Serializable
+ data class AllTypes(
+ val b: Byte,
+ val s: Short,
+ val i: Int,
+ val f: Float,
+ val d: Double,
+ val c: Char,
+ val B: Boolean,
+ val S: String
+ )
+
+ @Serializable
+ data class EnumWrapper(val e: Color)
+
+ @Serializable
+ sealed class Sealed {
+ @Serializable
+ data class One(val string: String) : Sealed()
+ }
+
+ @Serializable
+ class WithJsName(@JsName("b") val a: String)
+
+ @Serializable
+ data class WithSerialName(@SerialName("b") val a: String)
+
+ @Serializable
+ enum class Color {
+ RED,
+ GREEN,
+
+ @SerialName("red")
+ WITH_SERIALNAME_red
+ }
+
+ @Serializable(MyFancyClass.Companion::class)
+ data class MyFancyClass(val value: String) {
+
+ companion object : KSerializer<MyFancyClass> {
+
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MyFancyClass", PrimitiveKind.STRING)
+ override fun serialize(encoder: Encoder, value: MyFancyClass) {
+ encoder.encodeString("fancy ${value.value}")
+ }
+
+ override fun deserialize(decoder: Decoder): MyFancyClass {
+ return MyFancyClass(decoder.decodeString().removePrefix("fancy "))
+ }
+ }
+ }
+
+ @Test
+ fun dynamicSimpleTest() {
+ assertDynamicForm(Data(42)) { data, serialized ->
+ assertEquals(data.a, serialized.a)
+ }
+
+ assertDynamicForm(WithChar('c')) { data, serialized ->
+ assertEquals(data.a.toString(), serialized.a)
+ }
+
+ assertDynamicForm(AllTypes(1, 2, 3, 4.0f, 5.0, 'c', true, "string"))
+
+
+ assertDynamicForm(WithLong(5L))
+ assertDynamicForm(WithLong(MAX_SAFE_INTEGER.toLong()))
+ assertDynamicForm(WithLong(MAX_SAFE_INTEGER.unaryMinus().toLong()))
+ }
+
+ @Test
+ fun wrappedObjectsTest() {
+ assertDynamicForm(DataWrapper("a string", Data(42))) { data, serialized ->
+ assertEquals(data.s, serialized.s)
+ assertNotNull(serialized.d)
+ assertEquals(data.d?.a, serialized.d.a)
+ }
+ }
+
+ @Test
+ fun listTest() {
+ assertDynamicForm(listOf(1, 2, 3, 44), serializer = ListSerializer(Int.serializer())) { data, serialized ->
+ assertNotNull(serialized.length, "length property should exist")
+ assertEquals(data.size, serialized.length)
+
+ for (i in data.indices) {
+ assertEquals(data[i], serialized[i])
+ }
+ }
+ }
+
+ @Test
+ fun arrayTest() {
+ assertDynamicForm(intArrayOf(1, 2, 3, 44), serializer = IntArraySerializer(), true) { data, serialized ->
+ assertNotNull(serialized.length, "length property should exist")
+ assertEquals(data.size, serialized.length)
+
+ for (i in data.indices) {
+ assertEquals(data[i], serialized[i])
+ }
+ }
+ }
+
+ @Test
+ fun nestedListTest() {
+ assertDynamicForm(NestedList("a string", listOf(1, 2, 3, 44))) { data, serialized ->
+ assertEquals(data.a, serialized.a)
+ assertNotNull(serialized.list.length, "length property should exist")
+ assertEquals(data.list.size, serialized.list.length)
+
+ for (i in data.list.indices) {
+ assertEquals(data.list[i], serialized.list[i])
+ }
+ }
+
+ }
+
+ @Test
+ fun complexMapWrapperTest() {
+ assertDynamicForm(ComplexMapWrapper(mapOf("key1" to Data(1), "key2" to Data(2))))
+ }
+
+ @Test
+ fun mapWrapperTest() {
+ assertDynamicForm(MapWrapper(mapOf("key1" to 1, "key2" to 2)))
+ }
+
+ @Test
+ fun listOfListsTest() {
+ assertDynamicForm(
+ ListOfLists(
+ listOf(
+ listOf(Data(11), Data(12), Data(13)),
+ listOf(Data(21), Data(22))
+ )
+ )
+ ) { data, serialized ->
+ assertEquals(data.l.size, serialized.l.length)
+ assertEquals(data.l.first().size, serialized.l[0].length)
+ }
+ }
+
+ @Serializable
+ data class NestedCollections(val data: Map<String, Map<String, List<Int>>>)
+
+ @Test
+ fun nestedCollections() {
+ assertDynamicForm(
+ NestedCollections(
+ mapOf(
+ "one" to mapOf("oneone" to listOf(11, 12, 13), "onetwo" to listOf(1)),
+ "two" to mapOf("twotwo" to listOf(22, 23))
+ )
+ )
+ , serializer = NestedCollections.serializer()
+ )
+ }
+
+ @Test
+ fun enums() {
+ assertDynamicForm(EnumWrapper(Color.RED))
+ assertDynamicForm(Color.GREEN)
+ assertDynamicForm(Color.WITH_SERIALNAME_red) { _, serialized ->
+ assertEquals("red", serialized)
+ }
+ }
+
+ @Test
+ fun singlePrimitiveValue() {
+ assertDynamicForm("some string")
+ assertDynamicForm(1.toByte())
+ assertDynamicForm(1.toShort())
+ assertDynamicForm(1)
+ assertDynamicForm(1.toFloat())
+ assertDynamicForm(1.toDouble())
+ assertDynamicForm('c')
+ assertDynamicForm(true)
+ assertDynamicForm(false)
+ assertDynamicForm(1L)
+ val result = Json.encodeToDynamic(String.serializer().nullable, null)
+ assertEquals(null, result)
+ }
+
+ @Test
+ fun sealed() {
+ // test of sealed class but not polymorphic serialization
+ assertDynamicForm(Sealed.One("one"))
+ }
+
+ @Test
+ fun withSerialNam() {
+ assertDynamicForm(WithSerialName("something")) { data, serialized ->
+ assertEquals(data.a, serialized.b)
+ }
+ }
+
+ @Test
+ fun mapWithNullKey() {
+ val serialized = Json.encodeToDynamic(
+ MapSerializer(String.serializer().nullable, Int.serializer()),
+ mapOf(null to 0, "a" to 1)
+ )
+ assertNotNull(serialized[null], "null key should be present in output")
+ }
+
+ @Test
+ fun mapWithSimpleKey() {
+
+ inline fun <reified T> assertSimpleMapForm(key: T, value: String) {
+ assertDynamicForm(mapOf(key to value), MapSerializer(serializer(), String.serializer()))
+ }
+
+ assertSimpleMapForm(1, "Int 1")
+ assertSimpleMapForm("s", "String s")
+ assertSimpleMapForm('c', "char c")
+ assertSimpleMapForm(2.toByte(), "Byte 2")
+ assertSimpleMapForm(3.toShort(), "Short 3")
+ assertSimpleMapForm(4.toLong(), "Long 4")
+ assertSimpleMapForm(5.toFloat(), "Float 5")
+ assertSimpleMapForm(6.toDouble(), "Double 6")
+
+ assertDynamicForm(
+ mapOf(
+ Color.RED to "RED",
+ Color.GREEN to "GREEN",
+ Color.WITH_SERIALNAME_red to "red"
+ ),
+ MapSerializer(Color.serializer(), String.serializer())
+ ) { _, serialized ->
+ assertNotNull(serialized["red"], "WITH_SERIALNAME_red should be serialized as 'red'")
+ }
+ }
+
+ @Test
+ fun mapWithIllegalKey() {
+
+ val exception = assertFails {
+ Json.encodeToDynamic(
+ MapSerializer(Data.serializer(), String.serializer()),
+ mapOf(
+ Data(1) to "data",
+ Data(2) to "data",
+ Data(3) to "data"
+ )
+ )
+ }
+ assertEquals(IllegalArgumentException::class, exception::class)
+ assertTrue("should have a helpful error message") {
+ exception.message?.contains("can't be used in json as map key") == true
+ }
+
+ assertFails {
+ @Suppress("CAST_NEVER_SUCCEEDS")
+ assertDynamicForm(
+ mapOf(
+ (null as? Data) to "Data null"
+ ),
+ MapSerializer(Data.serializer().nullable, String.serializer())
+ )
+ }
+
+
+ val doubleSerializer = MapSerializer(Double.serializer(), String.serializer())
+ val value = mapOf(0.5 to "0.5")
+ var ex = assertFails {
+ assertDynamicForm(value, doubleSerializer)
+ }
+ assertTrue("should have a helpful error message") {
+ ex.message?.contains("can't be used in json as map key") == true
+ }
+
+ ex = assertFails {
+ assertDynamicForm(mapOf(Double.NaN to "NaN"), doubleSerializer)
+ }
+ assertTrue("should have a helpful error message") {
+ ex.message?.contains("can't be used in json as map key") == true
+ }
+
+ ex = assertFails {
+ assertDynamicForm(mapOf(Double.NEGATIVE_INFINITY to "NaN"), doubleSerializer)
+ }
+ assertTrue("should have a helpful error message") {
+ ex.message?.contains("can't be used in json as map key") == true
+ }
+
+ assertDynamicForm(mapOf(11.0 to "11"), doubleSerializer)
+
+ }
+
+ @Test
+ fun customSerializerTest() {
+ assertDynamicForm(MyFancyClass("apple"), MyFancyClass.serializer()) { _, serialized ->
+ assertEquals("fancy apple", serialized)
+ }
+
+ assertDynamicForm(
+ mapOf(MyFancyClass("apple") to "value"),
+ MapSerializer(MyFancyClass.serializer(), String.serializer())
+ ) { _, serialized ->
+ assertNotNull(serialized["fancy apple"], "should contain key 'fancy apple'")
+ }
+ }
+}
+
+public inline fun <reified T : Any> assertDynamicForm(
+ data: T,
+ serializer: KSerializer<T> = EmptySerializersModule().serializer(),
+ skipEqualsCheck: Boolean = false,
+ noinline assertions: ((T, dynamic) -> Unit)? = null
+) {
+ val serialized = Json.encodeToDynamic(serializer, data)
+ assertions?.invoke(data, serialized)
+ val string = Json.encodeToString(serializer, data)
+ assertEquals(
+ string,
+ JSON.stringify(serialized),
+ "JSON.stringify representation must be the same"
+ )
+
+ if (skipEqualsCheck) return // arrays etc.
+ assertEquals(data, Json.decodeFromString(serializer, string))
+}