• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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