1 package kotlinx.serialization 2 3 import org.junit.Test 4 import kotlinx.serialization.json.Json 5 import kotlinx.serialization.modules.SerializersModule 6 import kotlinx.serialization.modules.polymorphic 7 import kotlinx.serialization.modules.subclass 8 import kotlin.test.assertEquals 9 import kotlin.test.assertFailsWith 10 import kotlin.test.assertTrue 11 12 class JvmMissingFieldsExceptionTest { 13 @Serializable 14 data class Generic<out T1, out T2, out T3>(val f1: T1, val f2: T2, val f3: T3) 15 16 @Serializable 17 sealed class Parent(val p1: Int, val p2: Int = 2, val p3: Int) { 18 @Serializable 19 @SerialName("child") 20 data class Child(val c1: Int = 1, val c2: Int, val c3: Int = 3) : Parent(c1 + 1, 2, 3) 21 } 22 23 @Serializable 24 open class ShortPlaneClass(val f1: Int, val f2: Int, val f3: Int = 3, val f4: Int) 25 26 @Serializable 27 class WithTransient(val f1: Int, @Transient val f2: Int = 2, val f3: Int, val f4: Int) 28 29 @Serializable 30 abstract class SimpleAbstract(val p1: Int, val p2: Int) 31 32 @Serializable 33 @SerialName("a") 34 data class ChildA(val c1: Int, val c2: Int = 2, val c3: Int) : SimpleAbstract(0, 10) 35 36 @Serializable 37 data class PolymorphicWrapper(@Polymorphic val nested: SimpleAbstract) 38 39 @Serializable 40 class BigPlaneClass( 41 val f0: Int, 42 val f5: Int = 5, 43 val f6: Int, 44 val f7: Int = 7, 45 val f8: Int, 46 val f9: Int, 47 val f10: Int, 48 val f11: Int, 49 val f12: Int, 50 val f13: Int, 51 val f14: Int, 52 val f15: Int, 53 val f16: Int, 54 val f17: Int, 55 val f18: Int, 56 val f19: Int, 57 val f20: Int, 58 val f21: Int, 59 val f22: Int, 60 val f23: Int, 61 val f24: Int, 62 val f25: Int, 63 val f26: Int, 64 val f27: Int, 65 val f28: Int, 66 val f29: Int, 67 val f30: Int, 68 val f31: Int, 69 val f32: Int, 70 val f33: Int, 71 val f34: Int, 72 val f35: Int, 73 ) : ShortPlaneClass(1, 2, 3, 4) 74 75 @Test testShortPlaneClassnull76 fun testShortPlaneClass() { 77 assertFailsWithMessages(listOf("f2", "f4")) { 78 Json.decodeFromString<ShortPlaneClass>("""{"f1":1}""") 79 } 80 } 81 82 @Test testBigPlaneClassnull83 fun testBigPlaneClass() { 84 val missedFields = MutableList(36) { "f$it" } 85 val definedInJsonFields = arrayOf("f1", "f15", "f34") 86 val optionalFields = arrayOf("f3", "f5", "f7") 87 missedFields.removeAll(definedInJsonFields) 88 missedFields.removeAll(optionalFields) 89 assertFailsWithMessages(missedFields) { 90 Json.decodeFromString<BigPlaneClass>("""{"f1":1, "f15": 15, "f34": 34}""") 91 } 92 } 93 94 @Test testAnnotatedPolymorphicnull95 fun testAnnotatedPolymorphic() { 96 val module = SerializersModule { 97 polymorphic(SimpleAbstract::class, null) { 98 subclass(ChildA::class) 99 } 100 } 101 102 assertFailsWithMessages(listOf("p2", "c3")) { 103 Json { 104 serializersModule = module 105 }.decodeFromString<PolymorphicWrapper>("""{"nested": {"type": "a", "p1": 1, "c1": 11}}""") 106 } 107 } 108 109 110 @Test testSealednull111 fun testSealed() { 112 assertFailsWithMessages(listOf("p3", "c2")) { 113 Json.decodeFromString<Parent>("""{"type": "child", "p1":1, "c1": 11}""") 114 } 115 } 116 117 @Test testTransientnull118 fun testTransient() { 119 assertFailsWithMessages(listOf("f3", "f4")) { 120 Json.decodeFromString<WithTransient>("""{"f1":1}""") 121 } 122 } 123 124 @Test testGenericnull125 fun testGeneric() { 126 assertFailsWithMessages(listOf("f2", "f3")) { 127 Json.decodeFromString<Generic<Int, Int, Int>>("""{"f1":1}""") 128 } 129 } 130 131 assertFailsWithMessagesnull132 private inline fun assertFailsWithMessages(fields: List<String>, block: () -> Unit) { 133 val exception = assertFailsWith(MissingFieldException::class, null, block) 134 val missedMessages = fields.filter { !exception.message!!.contains(it) } 135 assertEquals(exception.missingFields.sorted(), fields.sorted()) 136 assertTrue(missedMessages.isEmpty(), "Expected message '${exception.message}' to contain substrings $fields") 137 } 138 } 139