1 /* 2 * 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.features 6 7 import kotlinx.serialization.* 8 import kotlinx.serialization.builtins.* 9 import kotlinx.serialization.features.sealed.SealedChild 10 import kotlinx.serialization.features.sealed.SealedParent 11 import kotlinx.serialization.internal.* 12 import kotlinx.serialization.json.* 13 import kotlinx.serialization.modules.* 14 import kotlin.test.* 15 16 class SealedClassesSerializationTest : JsonTestBase() { 17 @Serializable 18 sealed class SealedProtocol { 19 @Serializable 20 @SerialName("SealedProtocol.StringMessage") 21 data class StringMessage(val description: String, val message: String) : SealedProtocol() 22 23 @Serializable 24 @SerialName("SealedProtocol.IntMessage") 25 data class IntMessage(val description: String, val message: Int) : SealedProtocol() 26 27 @Serializable 28 @SerialName("SealedProtocol.ErrorMessage") 29 data class ErrorMessage(val error: String) : SealedProtocol() 30 31 @SerialName("EOF") 32 @Serializable 33 object EOF : SealedProtocol() 34 } 35 36 @Serializable 37 sealed class ProtocolWithAbstractClass { 38 39 @Serializable 40 @SerialName("ProtocolWithAbstractClass.Message") 41 abstract class Message : ProtocolWithAbstractClass() { 42 @Serializable 43 @SerialName("ProtocolWithAbstractClass.Message.StringMessage") 44 data class StringMessage(val description: String, val message: String) : Message() 45 46 @Serializable 47 @SerialName("ProtocolWithAbstractClass.Message.IntMessage") 48 data class IntMessage(val description: String, val message: Int) : Message() 49 } 50 51 @Serializable 52 @SerialName("ProtocolWithAbstractClass.ErrorMessage") 53 data class ErrorMessage(val error: String) : ProtocolWithAbstractClass() 54 55 @SerialName("EOF") 56 @Serializable 57 object EOF : ProtocolWithAbstractClass() 58 } 59 60 @Serializable 61 sealed class ProtocolWithSealedClass { 62 63 @Serializable 64 @SerialName("ProtocolWithSealedClass.Message") 65 sealed class Message : ProtocolWithSealedClass() { 66 @Serializable 67 @SerialName("ProtocolWithSealedClass.Message.StringMessage") 68 data class StringMessage(val description: String, val message: String) : Message() 69 70 @Serializable 71 @SerialName("ProtocolWithSealedClass.Message.IntMessage") 72 data class IntMessage(val description: String, val message: Int) : Message() 73 } 74 75 @Serializable 76 @SerialName("ProtocolWithSealedClass.ErrorMessage") 77 data class ErrorMessage(val error: String) : ProtocolWithSealedClass() 78 79 @SerialName("EOF") 80 @Serializable 81 object EOF : ProtocolWithSealedClass() 82 } 83 84 @Serializable 85 sealed class ProtocolWithGenericClass { 86 87 @Serializable 88 @SerialName("ProtocolWithGenericClass.Message") 89 data class Message<T>(val description: String, val message: T) : ProtocolWithGenericClass() 90 91 @Serializable 92 @SerialName("ProtocolWithGenericClass.ErrorMessage") 93 data class ErrorMessage(val error: String) : ProtocolWithGenericClass() 94 95 @SerialName("EOF") 96 @Serializable 97 object EOF : ProtocolWithGenericClass() 98 } 99 100 private val ManualSerializer: KSerializer<SimpleSealed> = SealedClassSerializer( 101 "SimpleSealed", 102 SimpleSealed::class, 103 arrayOf(SimpleSealed.SubSealedA::class, SimpleSealed.SubSealedB::class), 104 arrayOf(SimpleSealed.SubSealedA.serializer(), SimpleSealed.SubSealedB.serializer()) 105 ) 106 107 @Serializable 108 data class SealedHolder(val s: SimpleSealed) 109 110 @Serializable 111 data class SealedBoxHolder(val b: Box<SimpleSealed>) 112 <lambda>null113 private val arrayJson = Json { useArrayPolymorphism = true } 114 private val json = Json 115 116 @Test manualSerializernull117 fun manualSerializer() { 118 val message = json.encodeToString( 119 ManualSerializer, 120 SimpleSealed.SubSealedB(42) 121 ) 122 assertEquals("{\"type\":\"kotlinx.serialization.SimpleSealed.SubSealedB\",\"i\":42}", message) 123 } 124 125 @Test onTopLevelnull126 fun onTopLevel() { 127 val arrayMessage = arrayJson.encodeToString( 128 SimpleSealed.serializer(), 129 SimpleSealed.SubSealedB(42) 130 ) 131 val message = json.encodeToString( 132 SimpleSealed.serializer(), 133 SimpleSealed.SubSealedB(42) 134 ) 135 assertEquals("{\"type\":\"kotlinx.serialization.SimpleSealed.SubSealedB\",\"i\":42}", message) 136 assertEquals("[\"kotlinx.serialization.SimpleSealed.SubSealedB\",{\"i\":42}]", arrayMessage) 137 } 138 139 @Test insideClassnull140 fun insideClass() { 141 assertJsonFormAndRestored( 142 SealedHolder.serializer(), 143 SealedHolder(SimpleSealed.SubSealedA("foo")), 144 """{"s":{"type":"kotlinx.serialization.SimpleSealed.SubSealedA","s":"foo"}}""", 145 json 146 ) 147 } 148 149 @Test insideGenericnull150 fun insideGeneric() { 151 assertJsonFormAndRestored( 152 Box.serializer(SimpleSealed.serializer()), 153 Box<SimpleSealed>(SimpleSealed.SubSealedA("foo")), 154 """{"boxed":{"type":"kotlinx.serialization.SimpleSealed.SubSealedA","s":"foo"}}""", 155 json 156 ) 157 assertJsonFormAndRestored( 158 SealedBoxHolder.serializer(), 159 SealedBoxHolder(Box(SimpleSealed.SubSealedA("foo"))), 160 """{"b":{"boxed":{"type":"kotlinx.serialization.SimpleSealed.SubSealedA","s":"foo"}}}""", 161 json 162 ) 163 } 164 165 @Test complexProtocolnull166 fun complexProtocol() { 167 val messages = listOf<SealedProtocol>( 168 SealedProtocol.StringMessage("string message", "foo"), 169 SealedProtocol.IntMessage("int message", 42), 170 SealedProtocol.ErrorMessage("requesting termination"), 171 SealedProtocol.EOF 172 ) 173 val expected = 174 """[{"type":"SealedProtocol.StringMessage","description":"string message","message":"foo"},{"type":"SealedProtocol.IntMessage","description":"int message","message":42},{"type":"SealedProtocol.ErrorMessage","error":"requesting termination"},{"type":"EOF"}]""" 175 assertJsonFormAndRestored(ListSerializer(SealedProtocol.serializer()), messages, expected, json) 176 } 177 178 @Test protocolWithAbstractClassnull179 fun protocolWithAbstractClass() { 180 val messages = listOf<ProtocolWithAbstractClass>( 181 ProtocolWithAbstractClass.Message.StringMessage("string message", "foo"), 182 ProtocolWithAbstractClass.Message.IntMessage("int message", 42), 183 ProtocolWithAbstractClass.ErrorMessage("requesting termination"), 184 ProtocolWithAbstractClass.EOF 185 ) 186 val abstractContext = SerializersModule { 187 188 polymorphic(ProtocolWithAbstractClass::class) { 189 subclass(ProtocolWithAbstractClass.Message.IntMessage.serializer()) 190 subclass(ProtocolWithAbstractClass.Message.StringMessage.serializer()) 191 } 192 193 polymorphic(ProtocolWithAbstractClass.Message::class) { 194 subclass(ProtocolWithAbstractClass.Message.IntMessage.serializer()) 195 subclass(ProtocolWithAbstractClass.Message.StringMessage.serializer()) 196 } 197 } 198 val json = Json { 199 serializersModule = abstractContext 200 } 201 val expected = 202 """[{"type":"ProtocolWithAbstractClass.Message.StringMessage","description":"string message","message":"foo"},{"type":"ProtocolWithAbstractClass.Message.IntMessage","description":"int message","message":42},{"type":"ProtocolWithAbstractClass.ErrorMessage","error":"requesting termination"},{"type":"EOF"}]""" 203 assertJsonFormAndRestored(ListSerializer(ProtocolWithAbstractClass.serializer()), messages, expected, json) 204 } 205 206 @Test protocolWithSealedClassnull207 fun protocolWithSealedClass() { 208 val messages = listOf<ProtocolWithSealedClass>( 209 ProtocolWithSealedClass.Message.StringMessage("string message", "foo"), 210 ProtocolWithSealedClass.Message.IntMessage("int message", 42), 211 ProtocolWithSealedClass.ErrorMessage("requesting termination"), 212 ProtocolWithSealedClass.EOF 213 ) 214 val expected = 215 """[{"type":"ProtocolWithSealedClass.Message.StringMessage","description":"string message","message":"foo"},{"type":"ProtocolWithSealedClass.Message.IntMessage","description":"int message","message":42},{"type":"ProtocolWithSealedClass.ErrorMessage","error":"requesting termination"},{"type":"EOF"}]""" 216 assertJsonFormAndRestored(ListSerializer(ProtocolWithSealedClass.serializer()), messages, expected, json) 217 } 218 219 @Test partOfProtocolWithSealedClassnull220 fun partOfProtocolWithSealedClass() { 221 val messages = listOf<ProtocolWithSealedClass.Message>( 222 ProtocolWithSealedClass.Message.StringMessage("string message", "foo"), 223 ProtocolWithSealedClass.Message.IntMessage("int message", 42) 224 ) 225 val expected = 226 """[{"type":"ProtocolWithSealedClass.Message.StringMessage","description":"string message","message":"foo"},{"type":"ProtocolWithSealedClass.Message.IntMessage","description":"int message","message":42}]""" 227 228 assertJsonFormAndRestored(ListSerializer(ProtocolWithSealedClass.serializer()), messages, expected, json) 229 assertJsonFormAndRestored(ListSerializer(ProtocolWithSealedClass.Message.serializer()), messages, expected, json) 230 } 231 232 @Test protocolWithGenericClassnull233 fun protocolWithGenericClass() { 234 val messages = listOf<ProtocolWithGenericClass>( 235 ProtocolWithGenericClass.Message<String>("string message", "foo"), 236 ProtocolWithGenericClass.Message<Int>("int message", 42), 237 ProtocolWithGenericClass.ErrorMessage("requesting termination"), 238 ProtocolWithGenericClass.EOF 239 ) 240 val expected = 241 """[["ProtocolWithGenericClass.Message",{"description":"string message","message":["kotlin.String","foo"]}],["ProtocolWithGenericClass.Message",{"description":"int message","message":["kotlin.Int",42]}],["ProtocolWithGenericClass.ErrorMessage",{"error":"requesting termination"}],["EOF",{}]]""" 242 val json = Json { 243 useArrayPolymorphism = true 244 serializersModule = SerializersModule { 245 polymorphic(Any::class) { 246 subclass(Int::class) 247 subclass(String::class) 248 } 249 } 250 } 251 assertJsonFormAndRestored(ListSerializer(ProtocolWithGenericClass.serializer()), messages, expected, json) 252 } 253 254 @Test testSerializerLookupForSealedClassnull255 fun testSerializerLookupForSealedClass() { 256 val resSer = serializer<SealedProtocol>() 257 assertEquals(SealedProtocol::class, (resSer as AbstractPolymorphicSerializer).baseClass) 258 } 259 260 @Test testClassesFromDifferentFilesnull261 fun testClassesFromDifferentFiles() { 262 assertJsonFormAndRestored(SealedParent.serializer(), SealedChild(5), """{"type":"first child","i":1,"j":5}""") 263 } 264 } 265