xref: /aosp_15_r20/external/kotlinx.serialization/integration-test/src/commonTest/kotlin/sample/JsonTest.kt (revision 57b5a4a64c534cf7f27ac9427ceab07f3d8ed3d8)
1 /*
<lambda>null2  * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 @file:Suppress("UNCHECKED_CAST")
5 
6 package sample
7 
8 import kotlinx.serialization.*
9 import kotlinx.serialization.builtins.*
10 import kotlinx.serialization.json.*
11 import kotlinx.serialization.modules.*
12 import kotlin.reflect.*
13 import kotlin.test.*
14 
15 val jsonWithDefaults = Json { encodeDefaults = true }
16 
17 class JsonTest {
18 
19     private val originalData = Data("Hello")
20     private val originalString =
21         """{"s":"Hello","box":{"boxed":42},"boxes":{"desc":"boxes","boxes":[{"boxed":"foo"},{"boxed":"bar"}]},"m":{}}"""
<lambda>null22     private val nonstrict: Json = Json {
23         isLenient = true
24         ignoreUnknownKeys = true
25         allowSpecialFloatingPointValues = true
26         useArrayPolymorphism = true
27         encodeDefaults = true
28     }
29 
30     @Test
testStringFormnull31     fun testStringForm() {
32         val str = jsonWithDefaults.encodeToString(Data.serializer(), originalData)
33         assertEquals(originalString, str)
34     }
35 
36     @Test
testSerializeBacknull37     fun testSerializeBack() {
38         val restored = jsonWithDefaults.decodeFromString(Data.serializer(), originalString)
39         assertEquals(originalData, restored)
40     }
41 
genTestDatanull42     private fun genTestData(): Holder {
43         var cnt = -1
44         fun gen(): MessageWithId {
45             cnt++
46             return MessageWithId(cnt, "Message #$cnt")
47         }
48 
49         return Holder(gen(), listOf(gen(), gen()), gen(), setOf(SimpleMessage()), DoubleSimpleMessage("DoubleSimple"), gen())
50     }
51 
<lambda>null52     private val testModule = SerializersModule {
53         listOf(Message::class, IMessage::class, SimpleMessage::class).forEach { clz ->
54             polymorphic(clz as KClass<Any>) {
55                 subclass(SimpleMessage::class)
56                 subclass(DoubleSimpleMessage::class)
57                 subclass(MessageWithId::class)
58             }
59         }
60     }
61 
62     @Test
testEnablesImplicitlyOnInterfacesAndAbstractClassesnull63     fun testEnablesImplicitlyOnInterfacesAndAbstractClasses() {
64         val json = Json(jsonWithDefaults) { useArrayPolymorphism = true; prettyPrint = false; serializersModule = testModule }
65         val data = genTestData()
66         assertEquals("""{"iMessage":["MessageWithId",{"id":0,"body":"Message #0"}],"iMessageList":[["MessageWithId",{"id":1,"body":"Message #1"}],""" +
67                 """["MessageWithId",{"id":2,"body":"Message #2"}]],"message":["MessageWithId",{"id":3,"body":"Message #3"}],"msgSet":[["SimpleMessage",""" +
68                 """{"body":"Simple"}]],"simple":["DoubleSimpleMessage",{"body":"Simple","body2":"DoubleSimple"}],"withId":{"id":4,"body":"Message #4"}}""",
69             json.encodeToString(Holder.serializer(), data)
70         )
71     }
72 
73     @Test
testPolymorphicForGenericUpperBoundnull74     fun testPolymorphicForGenericUpperBound() {
75         if (Platform.name == "JS") return // Does not work with JS IR, see #1072
76         val generic = GenericMessage<Message, Any>(MessageWithId(42, "body"), "body2")
77         val serial = GenericMessage.serializer(Message.serializer(), Int.serializer() as KSerializer<Any>)
78         val json = Json {
79             useArrayPolymorphism = true
80             prettyPrint = false
81             serializersModule = testModule + SerializersModule {
82                 polymorphic(Any::class) {
83                     subclass(Int::class)
84                     subclass(String::class)
85                 }
86             }
87         }
88         val s = json.encodeToString(serial, generic)
89         assertEquals("""{"value":["MessageWithId",{"id":42,"body":"body"}],"value2":["kotlin.String","body2"]}""", s)
90     }
91 
92     @Test
93     @OptIn(ExperimentalSerializationApi::class)
testDescriptornull94     fun testDescriptor() {
95         val desc = Holder.serializer().descriptor
96         assertEquals(PolymorphicSerializer(IMessage::class).descriptor, desc.getElementDescriptor(0))
97     }
98 
99     @Test
canBeSerializedAsDerivednull100     fun canBeSerializedAsDerived() {
101         val derived = Derived(42)
102         val msg = jsonWithDefaults.encodeToString(Derived.serializer(), derived)
103         assertEquals("""{"publicState":"A","privateState":"B","derivedState":42,"rootState":"foo"}""", msg)
104         val d2 = jsonWithDefaults.decodeFromString(Derived.serializer(), msg)
105         assertEquals(derived, d2)
106     }
107 
108     @Test
canBeSerializedAsParentnull109     fun canBeSerializedAsParent() {
110         val derived = Derived(42)
111         val msg = jsonWithDefaults.encodeToString(SerializableBase.serializer(), derived)
112         assertEquals("""{"publicState":"A","privateState":"B"}""", msg)
113         val d2 = jsonWithDefaults.decodeFromString(SerializableBase.serializer(), msg)
114         assertEquals(SerializableBase(), d2)
115         // no derivedState
116         assertFailsWith<SerializationException> { jsonWithDefaults.decodeFromString(Derived.serializer(), msg) }
117     }
118 
119     @Test
testWithOpenPropertynull120     fun testWithOpenProperty() {
121         val d = Derived2("foo")
122         val msgFull = Json.encodeToString(Derived2.serializer(), d)
123         assertEquals("""{"state1":"foo","state2":"foo"}""", msgFull)
124         assertEquals("""{"state1":"foo"}""", Json.encodeToString(Base1.serializer(), d))
125         val restored = Json.decodeFromString(Derived2.serializer(), msgFull)
126         val restored2 =
127             Json.decodeFromString(Derived2.serializer(), """{"state1":"bar","state2":"foo"}""") // state1 is ignored anyway
128         assertEquals("""Derived2(state1='foo')""", restored.toString())
129         assertEquals("""Derived2(state1='foo')""", restored2.toString())
130     }
131 
checkNotRegisteredMessagenull132     private fun checkNotRegisteredMessage(exception: SerializationException) {
133         val expectedText =
134             "is not found in the polymorphic scope of"
135         assertEquals(true, exception.message?.contains(expectedText))
136     }
137 
138     @Test
failWithoutModulesWithCustomClassnull139     fun failWithoutModulesWithCustomClass() {
140         checkNotRegisteredMessage(
141             assertFailsWith<SerializationException>("not registered") {
142                 Json.encodeToString(
143                     MyPolyData.serializer(),
144                     MyPolyData(mapOf("a" to IntData(42)))
145                 )
146             }
147         )
148     }
149 
150     @Test
testWithModulesnull151     fun testWithModules() {
152         val json = Json {
153             useArrayPolymorphism = true; serializersModule = SerializersModule { polymorphic(Any::class) { subclass(IntData::class) } } }
154         assertStringFormAndRestored(
155             expected = """{"data":{"a":["sample.IntData",{"intV":42}]}}""",
156             original = MyPolyData(mapOf("a" to IntData(42))),
157             serializer = MyPolyData.serializer(),
158             format = json
159         )
160     }
161 
162     /**
163      * This test should fail because PolyDerived registered in the scope of PolyBase, not kotlin.Any
164      */
165     @Test
failWithModulesNotInAnyScopenull166     fun failWithModulesNotInAnyScope() {
167         val json = Json { serializersModule = BaseAndDerivedModule }
168         checkNotRegisteredMessage(
169             assertFailsWith<SerializationException> {
170                 json.encodeToString(
171                     MyPolyData.serializer(),
172                     MyPolyData(mapOf("a" to PolyDerived("foo")))
173                 )
174             }
175         )
176     }
177 
<lambda>null178     private val baseAndDerivedModuleAtAny = SerializersModule {
179         polymorphic(Any::class) {
180             subclass(PolyDerived::class)
181         }
182     }
183 
184 
185     @Test
testRebindModulesnull186     fun testRebindModules() {
187         val json = Json { useArrayPolymorphism = true; serializersModule =  baseAndDerivedModuleAtAny }
188         assertStringFormAndRestored(
189             expected = """{"data":{"a":["sample.PolyDerived",{"id":1,"s":"foo"}]}}""",
190             original = MyPolyData(mapOf("a" to PolyDerived("foo"))),
191             serializer = MyPolyData.serializer(),
192             format = json
193         )
194     }
195 
196     /**
197      * This test should fail because PolyDerived registered in the scope of kotlin.Any, not PolyBase
198      */
199     @Test
failWithModulesNotInParticularScopenull200     fun failWithModulesNotInParticularScope() {
201         val json = Json { serializersModule = baseAndDerivedModuleAtAny }
202         checkNotRegisteredMessage(
203             assertFailsWith<SerializationException> {
204                 json.encodeToString(
205                     MyPolyDataWithPolyBase.serializer(),
206                     MyPolyDataWithPolyBase(mapOf("a" to PolyDerived("foo")), PolyDerived("foo"))
207                 )
208             }
209         )
210     }
211 
212     @Test
testBindModulesnull213     fun testBindModules() {
214         val json = Json { useArrayPolymorphism = true; serializersModule = (baseAndDerivedModuleAtAny + BaseAndDerivedModule) }
215         assertStringFormAndRestored(
216             expected = """{"data":{"a":["sample.PolyDerived",{"id":1,"s":"foo"}]},"polyBase":["sample.PolyDerived",{"id":1,"s":"foo"}]}""",
217             original = MyPolyDataWithPolyBase(mapOf("a" to PolyDerived("foo")), PolyDerived("foo")),
218             serializer = MyPolyDataWithPolyBase.serializer(),
219             format = json
220         )
221     }
222 
223     @Test
geoTestnull224     fun geoTest() {
225         val deser = nonstrict.decodeFromString(GeoCoordinate.serializer(), """{"latitude":1.0,"longitude":1.0}""")
226         assertEquals(GeoCoordinate(1.0, 1.0), deser)
227     }
228 
229     @Test
geoTest2null230     fun geoTest2() {
231         val deser = nonstrict.decodeFromString(GeoCoordinate.serializer(), """{}""")
232         assertEquals(GeoCoordinate(0.0, 0.0), deser)
233     }
234 
235     @Test
geoTestValidationnull236     fun geoTestValidation() {
237         assertFailsWith<IllegalArgumentException> {
238             nonstrict.decodeFromString(GeoCoordinate.serializer(), """{"latitude":-1.0,"longitude":1.0}""")
239         }
240     }
241 }
242 
assertStringFormAndRestorednull243 inline fun <reified T : Any> assertStringFormAndRestored(
244     expected: String,
245     original: T,
246     serializer: KSerializer<T>,
247     format: Json = jsonWithDefaults,
248     printResult: Boolean = false
249 ) {
250     val string = format.encodeToString(serializer, original)
251     if (printResult) println("[Serialized form] $string")
252     assertEquals(expected, string)
253     val restored = format.decodeFromString(serializer, string)
254     if (printResult) println("[Restored form] $original")
255     assertEquals(original, restored)
256 }
257