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