• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.serialization.json
6 
7 import kotlinx.serialization.*
8 import kotlinx.serialization.descriptors.*
9 import kotlinx.serialization.encoding.*
10 import kotlin.test.*
11 
12 class JsonEncoderDecoderRecursiveTest : JsonTestBase() {
13     private val inputDataString = """{"id":0,"payload":{"from":42,"to":43,"msg":"Hello world"},"timestamp":1000}"""
14     private val inputErrorString = """{"id":1,"payload":{"error":"Connection timed out"},"timestamp":1001}"""
15     private val inputDataJson = default.parseToJsonElement(inputDataString)
16     private val inputErrorJson = default.parseToJsonElement(inputErrorString)
17     private val inputRecursive =
18         """{"type":"b","children":[{"type":"a","value":1},{"type":"a","value":2},{"type":"b","children":[]}]}"""
19     private val outputRecursive = SealedRecursive.B(
20         listOf(SealedRecursive.A(1), SealedRecursive.A(2), SealedRecursive.B(emptyList()))
21     )
22 
23     @Test
24     fun testParseDataString() = parametrizedTest { streaming ->
25         val ev = default.decodeFromString(Event.serializer(), inputDataString, streaming)
26         with(ev) {
27             assertEquals(0, id)
28             assertEquals(Either.Right(Payload(42, 43, "Hello world")), payload)
29             assertEquals(1000, timestamp)
30         }
31     }
32 
33     @Test
34     fun testParseErrorString() = parametrizedTest { jsonTestingMode ->
35         val ev = default.decodeFromString(Event.serializer(), inputErrorString, jsonTestingMode)
36         with(ev) {
37             assertEquals(1, id)
38             assertEquals(Either.Left("Connection timed out"), payload)
39             assertEquals(1001, timestamp)
40         }
41     }
42 
43     @Test
44     fun testWriteDataString() = parametrizedTest { jsonTestingMode ->
45         val outputData = Event(0, Either.Right(Payload(42, 43, "Hello world")), 1000)
46         val ev = default.encodeToString(Event.serializer(), outputData, jsonTestingMode)
47         assertEquals(inputDataString, ev)
48     }
49 
50     @Test
51     fun testWriteDataStringIndented() = parametrizedTest { jsonTestingMode ->
52         val outputData = Event(0, Either.Right(Payload(42, 43, "Hello world")), 1000)
53         val ev = Json { prettyPrint = true }.encodeToString(Event.serializer(), outputData, jsonTestingMode)
54         assertEquals("""{
55             |    "id": 0,
56             |    "payload": {
57             |        "from": 42,
58             |        "to": 43,
59             |        "msg": "Hello world"
60             |    },
61             |    "timestamp": 1000
62             |}""".trimMargin(), ev)
63     }
64 
65     @Test
66     fun testWriteErrorString() = parametrizedTest { jsonTestingMode ->
67         val outputError = Event(1, Either.Left("Connection timed out"), 1001)
68         val ev = default.encodeToString(Event.serializer(), outputError, jsonTestingMode)
69         assertEquals(inputErrorString, ev)
70     }
71 
72     @Test
73     fun testParseDataJson() {
74         val ev = default.decodeFromJsonElement(Event.serializer(), inputDataJson)
75         with(ev) {
76             assertEquals(0, id)
77             assertEquals(Either.Right(Payload(42, 43, "Hello world")), payload)
78             assertEquals(1000, timestamp)
79         }
80     }
81 
82     @Test
83     fun testParseErrorJson() {
84         val ev = default.decodeFromJsonElement(Event.serializer(), inputErrorJson)
85         with(ev) {
86             assertEquals(1, id)
87             assertEquals(Either.Left("Connection timed out"), payload)
88             assertEquals(1001, timestamp)
89         }
90     }
91 
92     @Test
93     fun testWriteDataJson() {
94         val outputData = Event(0, Either.Right(Payload(42, 43, "Hello world")), 1000)
95         val ev = default.encodeToJsonElement(Event.serializer(), outputData)
96         assertEquals(inputDataJson, ev)
97     }
98 
99     @Test
100     fun testWriteErrorJson() {
101         val outputError = Event(1, Either.Left("Connection timed out"), 1001)
102         val ev = default.encodeToJsonElement(Event.serializer(), outputError)
103         assertEquals(inputErrorJson, ev)
104     }
105 
106     @Test
107     fun testParseRecursive() = parametrizedTest { jsonTestingMode ->
108         val ev = default.decodeFromString(RecursiveSerializer, inputRecursive, jsonTestingMode)
109         assertEquals(outputRecursive, ev)
110     }
111 
112     @Test
113     fun testWriteRecursive() = parametrizedTest { jsonTestingMode ->
114         val ev = default.encodeToString(RecursiveSerializer, outputRecursive, jsonTestingMode)
115         assertEquals(inputRecursive, ev)
116     }
117 
118     @Serializable
119     private data class Payload(val from: Long, val to: Long, val msg: String)
120 
121     private sealed class Either {
122         data class Left(val errorMsg: String): Either()
123         data class Right(val data: Payload): Either()
124     }
125 
126     private object EitherSerializer: KSerializer<Either> {
127         override val descriptor: SerialDescriptor = buildSerialDescriptor("Either", PolymorphicKind.SEALED) {
128             val leftDescriptor = buildClassSerialDescriptor("Either.Left") {
129                 element<String>("errorMsg")
130             }
131             val rightDescriptor = buildClassSerialDescriptor("Either.Right") {
132                 element("data", Payload.serializer().descriptor)
133             }
134             element("left", leftDescriptor)
135             element("right", rightDescriptor)
136         }
137 
138         override fun deserialize(decoder: Decoder): Either {
139             val jsonReader = decoder as? JsonDecoder
140                     ?: throw SerializationException("This class can be loaded only by JSON")
141             val tree = jsonReader.decodeJsonElement() as? JsonObject
142                     ?: throw SerializationException("Expected JSON object")
143             if ("error" in tree) return Either.Left(tree.getValue("error").jsonPrimitive.content)
144             return Either.Right(decoder.json.decodeFromJsonElement(Payload.serializer(), tree))
145         }
146 
147         override fun serialize(encoder: Encoder, value: Either) {
148             val jsonWriter = encoder as? JsonEncoder
149                     ?: throw SerializationException("This class can be saved only by JSON")
150             val tree = when (value) {
151                 is Either.Left -> JsonObject(mapOf("error" to JsonPrimitive(value.errorMsg)))
152                 is Either.Right -> encoder.json.encodeToJsonElement(Payload.serializer(), value.data)
153             }
154             jsonWriter.encodeJsonElement(tree)
155         }
156     }
157 
158     @Serializable
159     private data class Event(
160         val id: Int,
161         @Serializable(with = EitherSerializer::class) val payload: Either,
162         val timestamp: Long
163     )
164 
165     @Serializable(with = RecursiveSerializer::class)
166     private sealed class SealedRecursive {
167         @Serializable
168         data class A(val value: Int) : SealedRecursive()
169 
170         @Serializable
171         data class B(val children: List<SealedRecursive>) : SealedRecursive()
172     }
173 
174     private object RecursiveSerializer : KSerializer<SealedRecursive> {
175         private const val typeAttribute = "type"
176         private const val typeNameA = "a"
177         private const val typeNameB = "b"
178 
179         // TODO in builder is not suitable for recursive descriptors
180         override val descriptor: SerialDescriptor = buildSerialDescriptor("SealedRecursive", PolymorphicKind.SEALED) {
181             element("a", SealedRecursive.A.serializer().descriptor)
182             element("b", SealedRecursive.B.serializer().descriptor)
183         }
184 
185         override fun serialize(encoder: Encoder, value: SealedRecursive) {
186             if (encoder !is JsonEncoder) throw SerializationException("This class can be saved only by JSON")
187             val (tree, typeName) = when (value) {
188                 is SealedRecursive.A -> encoder.json.encodeToJsonElement(SealedRecursive.A.serializer(), value) to typeNameA
189                 is SealedRecursive.B -> encoder.json.encodeToJsonElement(SealedRecursive.B.serializer(), value) to typeNameB
190             }
191             val contents: MutableMap<String, JsonElement> = mutableMapOf(typeAttribute to JsonPrimitive(typeName))
192             contents.putAll(tree.jsonObject)
193             val element = JsonObject(contents)
194             encoder.encodeJsonElement(element)
195         }
196 
197         override fun deserialize(decoder: Decoder): SealedRecursive {
198             val jsonReader = decoder as? JsonDecoder
199                 ?: throw SerializationException("This class can be loaded only by JSON")
200             val tree = jsonReader.decodeJsonElement() as? JsonObject
201                 ?: throw SerializationException("Expected JSON object")
202             val typeName = tree.getValue(typeAttribute).jsonPrimitive.content
203             val objTree = JsonObject(tree - typeAttribute)
204             return when (typeName) {
205                 typeNameA -> decoder.json.decodeFromJsonElement(SealedRecursive.A.serializer(), objTree)
206                 typeNameB -> decoder.json.decodeFromJsonElement(SealedRecursive.B.serializer(), objTree)
207                 else -> throw SerializationException("Unknown type: $typeName")
208             }
209         }
210     }
211 }
212