1 /* 2 * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 package sample 5 6 import kotlinx.serialization.* 7 import kotlinx.serialization.descriptors.SerialDescriptor 8 import kotlinx.serialization.encoding.AbstractDecoder 9 import kotlinx.serialization.encoding.AbstractEncoder 10 import kotlinx.serialization.encoding.CompositeDecoder 11 import kotlinx.serialization.encoding.CompositeEncoder 12 import kotlinx.serialization.modules.* 13 import kotlin.test.Test 14 import kotlin.test.assertEquals 15 import kotlin.test.assertNotSame 16 17 class BasicTypesSerializationTest { 18 19 enum class Attitude { POSITIVE, NEUTRAL, NEGATIVE } 20 21 @Serializable 22 data class Tree(val name: String, val left: Tree? = null, val right: Tree? = null) 23 24 @Serializable 25 data class TypesUmbrella( 26 val unit: Unit, 27 val boolean: Boolean, 28 val byte: Byte, 29 val short: Short, 30 val int: Int, 31 val long: Long, 32 val float: Float, 33 val double: Double, 34 val char: Char, 35 val string: String, 36 val enum: Attitude, 37 val intData: IntData, 38 val unitN: Unit?, 39 val booleanN: Boolean?, 40 val byteN: Byte?, 41 val shortN: Short?, 42 val intN: Int?, 43 val longN: Long?, 44 val floatN: Float?, 45 val doubleN: Double?, 46 val charN: Char?, 47 val stringN: String?, 48 val enumN: Attitude?, 49 val intDataN: IntData?, 50 val listInt: List<Int>, 51 val listIntN: List<Int?>, 52 val listNInt: Set<Int>?, 53 val listNIntN: MutableSet<Int?>?, 54 val listListEnumN: List<List<Attitude?>>, 55 val listIntData: List<IntData>, 56 val listIntDataN: MutableList<IntData?>, 57 val tree: Tree, 58 val mapStringInt: Map<String, Int>, 59 val mapIntStringN: Map<Int, String?>, 60 val arrays: ArraysUmbrella 61 ) 62 63 @Serializable 64 data class ArraysUmbrella( 65 val arrByte: Array<Byte>, 66 val arrInt: Array<Int>, 67 val arrIntN: Array<Int?>, 68 val arrIntData: Array<IntData> 69 ) { equalsnull70 override fun equals(other: Any?) = other is ArraysUmbrella && 71 arrByte.contentEquals(other.arrByte) && 72 arrInt.contentEquals(other.arrInt) && 73 arrIntN.contentEquals(other.arrIntN) && 74 arrIntData.contentEquals(other.arrIntData) 75 } 76 77 val data = TypesUmbrella( 78 Unit, true, 10, 20, 30, 40, 50f, 60.0, 'A', "Str0", Attitude.POSITIVE, IntData(70), 79 null, null, 11, 21, 31, 41, 51f, 61.0, 'B', "Str1", Attitude.NEUTRAL, null, 80 listOf(1, 2, 3), 81 listOf(4, 5, null), 82 setOf(6, 7, 8), 83 mutableSetOf(null, 9, 10), 84 listOf(listOf(Attitude.NEGATIVE, null)), 85 listOf(IntData(1), IntData(2), IntData(3)), 86 mutableListOf(IntData(1), null, IntData(3)), 87 Tree("root", Tree("left"), Tree("right", Tree("right.left"), Tree("right.right"))), 88 mapOf("one" to 1, "two" to 2, "three" to 3), 89 mapOf(0 to null, 1 to "first", 2 to "second"), 90 ArraysUmbrella( 91 arrayOf(1, 2, 3), 92 arrayOf(100, 200, 300), 93 arrayOf(null, -1, -2), 94 arrayOf(IntData(1), IntData(2)) 95 ) 96 ) 97 98 @Serializable 99 class WithSecondaryConstructor(var someProperty: Int) { 100 var test: String = "Test" 101 102 constructor() : this(42) 103 104 override fun equals(other: Any?): Boolean { 105 if (this === other) return true 106 if (other !is WithSecondaryConstructor) return false 107 108 if (someProperty != other.someProperty) return false 109 if (test != other.test) return false 110 111 return true 112 } 113 114 override fun hashCode(): Int { 115 var result = someProperty 116 result = 31 * result + test.hashCode() 117 return result 118 } 119 } 120 121 122 // KeyValue Input/Output 123 124 @OptIn(ExperimentalSerializationApi::class) 125 class KeyValueOutput(val sb: StringBuilder) : AbstractEncoder() { 126 127 override val serializersModule: SerializersModule = EmptySerializersModule() 128 beginStructurenull129 override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder { 130 sb.append('{') 131 return this 132 } 133 endStructurenull134 override fun endStructure(descriptor: SerialDescriptor) { 135 sb.append('}') 136 } 137 encodeElementnull138 override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean { 139 if (index > 0) sb.append(", ") 140 sb.append(descriptor.getElementName(index)) 141 sb.append(':') 142 return true 143 } 144 encodeNullnull145 override fun encodeNull() { 146 sb.append("null") 147 } 148 encodeValuenull149 override fun encodeValue(value: Any) { 150 sb.append(value) 151 } 152 encodeStringnull153 override fun encodeString(value: String) { 154 sb.append('"') 155 sb.append(value) 156 sb.append('"') 157 } 158 encodeCharnull159 override fun encodeChar(value: Char) = encodeString(value.toString()) 160 } 161 162 class KeyValueInput(val inp: Parser) : AbstractDecoder() { 163 164 override val serializersModule: SerializersModule = EmptySerializersModule() 165 166 override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder { 167 inp.expectAfterWhiteSpace('{') 168 return this 169 } 170 171 override fun endStructure(descriptor: SerialDescriptor) = inp.expectAfterWhiteSpace('}') 172 173 override fun decodeElementIndex(descriptor: SerialDescriptor): Int { 174 inp.skipWhitespace(',') 175 val name = inp.nextUntil(':', '}') 176 if (name.isEmpty()) 177 return CompositeDecoder.DECODE_DONE 178 val index = descriptor.getElementIndex(name) 179 inp.expect(':') 180 return index 181 } 182 183 private fun readToken(): String { 184 inp.skipWhitespace() 185 return inp.nextUntil(' ', ',', '}') 186 } 187 188 override fun decodeNotNullMark(): Boolean { 189 inp.skipWhitespace() 190 if (inp.cur != 'n'.code) return true 191 return false 192 } 193 194 override fun decodeNull(): Nothing? { 195 check(readToken() == "null") { "'null' expected" } 196 return null 197 } 198 199 override fun decodeBoolean(): Boolean = readToken() == "true" 200 override fun decodeByte(): Byte = readToken().toByte() 201 override fun decodeShort(): Short = readToken().toShort() 202 override fun decodeInt(): Int = readToken().toInt() 203 override fun decodeLong(): Long = readToken().toLong() 204 override fun decodeFloat(): Float = readToken().toFloat() 205 override fun decodeDouble(): Double = readToken().toDouble() 206 207 override fun decodeEnum(enumDescriptor: SerialDescriptor): Int { 208 return readToken().toInt() 209 } 210 211 override fun decodeString(): String { 212 inp.expectAfterWhiteSpace('"') 213 val value = inp.nextUntil('"') 214 inp.expect('"') 215 return value 216 } 217 218 override fun decodeChar(): Char = decodeString().single() 219 } 220 221 // Very simple char-by-char parser 222 class Parser(private val inp: StringReader) { 223 var cur: Int = inp.read() 224 nextnull225 fun next() { 226 cur = inp.read() 227 } 228 skipWhitespacenull229 fun skipWhitespace(vararg c: Char) { 230 while (cur >= 0 && (cur.toChar().isWhitespace() || cur.toChar() in c)) 231 next() 232 } 233 expectnull234 fun expect(c: Char) { 235 check(cur == c.code) { "Expected '$c'" } 236 next() 237 } 238 expectAfterWhiteSpacenull239 fun expectAfterWhiteSpace(c: Char) { 240 skipWhitespace() 241 expect(c) 242 } 243 nextUntilnull244 fun nextUntil(vararg c: Char): String { 245 val sb = StringBuilder() 246 while (cur >= 0 && cur.toChar() !in c) { 247 sb.append(cur.toChar()) 248 next() 249 } 250 return sb.toString() 251 } 252 } 253 254 255 class StringReader(val str: String) { 256 private var position: Int = 0 readnull257 fun read(): Int = when (position) { 258 str.length -> -1 259 else -> str[position++].code 260 } 261 } 262 263 @Test testKvSerializationnull264 fun testKvSerialization() { 265 // serialize to string 266 val sb = StringBuilder() 267 val out = KeyValueOutput(sb) 268 out.encodeSerializableValue(TypesUmbrella.serializer(), data) 269 // deserialize from string 270 val str = sb.toString() 271 val inp = KeyValueInput(Parser(StringReader(str))) 272 val other = inp.decodeSerializableValue(TypesUmbrella.serializer()) 273 // assert we've got it back from string 274 assertEquals(data, other) 275 assertNotSame(data, other) 276 } 277 278 @Test someConstructornull279 fun someConstructor() { 280 assertStringFormAndRestored("""{"someProperty":42,"test":"Test"}""", WithSecondaryConstructor(), WithSecondaryConstructor.serializer()) 281 } 282 } 283