Created
July 23, 2019 16:23
-
-
Save sandwwraith/3d736a5ed6a51ede4a291b7bbb482733 to your computer and use it in GitHub Desktop.
Samples for kotlinx.serialization talk (WIP)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper | |
import com.fasterxml.jackson.module.kotlin.readValue | |
import com.google.gson.Gson | |
import com.google.gson.reflect.TypeToken | |
import kotlinx.serialization.* | |
import kotlinx.serialization.CompositeDecoder.Companion.READ_DONE | |
import kotlinx.serialization.internal.HexConverter | |
import kotlinx.serialization.json.* | |
import kotlinx.serialization.protobuf.ProtoBuf | |
import org.junit.Test | |
import java.security.MessageDigest | |
import java.sql.PreparedStatement | |
import java.sql.ResultSet | |
import kotlin.test.assertEquals | |
@Serializable | |
data class Data( | |
val data: String, | |
val optionalData: String = "empty" | |
) | |
@Serializable | |
data class GenericData<T>(val description: String, val data: T) | |
private const val inputString = """{"data":"Foo"}""" | |
private const val inputGenericString = """{"description":"wrapped data","data":{"data":"Bar"}}""" | |
private const val inputListString = """[{"data":"Bar"}]""" | |
class TalkSamples { | |
@Test | |
fun kxSerialization() { | |
val serializer: KSerializer<Data> = Data.Companion.serializer() | |
val data = Json.parse<Data>(serializer, inputString) | |
// Data(data=Foo, optionalData=empty) | |
println(data) | |
} | |
@Test | |
fun gson() { | |
val data = Gson().fromJson(inputString, Data::class.java) | |
// Data(data=Foo, optionalData=null) | |
println(data) | |
} | |
@Test | |
fun jackson() { | |
val data = jacksonObjectMapper().readValue<Data>(inputString) | |
println(data) | |
println(jacksonObjectMapper().writeValueAsString(Data("Foo"))) | |
} | |
@Test | |
fun gsonGeneric() { | |
val data: GenericData<Data> = Gson().fromJson( | |
inputGenericString, | |
(object : TypeToken<GenericData<Data>>() {}).type | |
) | |
println(data) | |
} | |
@Test | |
fun gsonGenericList() { | |
val data: List<Data> = Gson().fromJson( | |
inputListString, | |
(object : TypeToken<List<Data>>() {}).type | |
) | |
println(data) | |
} | |
@Test | |
fun kxGeneric() { | |
val json = Json(JsonConfiguration( | |
encodeDefaults = false, | |
unquoted = true, | |
prettyPrint = true | |
)) | |
val protobuf = ProtoBuf() | |
val data = json.parse( | |
GenericData.serializer(Data.serializer()), | |
inputGenericString | |
) | |
println(data) | |
println(protobuf) | |
} | |
@Test | |
fun descriptors() { | |
println(GenericData.serializer(Data.serializer()).descriptor.dump()) | |
println(JsonSchema(Data.serializer().descriptor)) | |
} | |
fun JsonSchema(descriptor: SerialDescriptor): JsonObject { | |
val properties = mutableMapOf<String, kotlinx.serialization.json.JsonElement>() | |
descriptor.elementDescriptors().forEachIndexed { index, child -> | |
val elementName = descriptor.getElementName(index) | |
properties[elementName] = JsonSchema(child) | |
} | |
val objectData = mutableMapOf<String, JsonElement>( | |
"description" to JsonLiteral(descriptor.name) | |
) | |
if (properties.isNotEmpty()) objectData["properties"] = JsonObject(properties) | |
return JsonObject(objectData) | |
} | |
fun SerialDescriptor.dump(): String { | |
fun SerialDescriptor.dumpInt(): List<String> { | |
val builder = mutableListOf<String>() | |
builder.add("{") | |
elementDescriptors().forEachIndexed { i, it -> | |
val inners = it.dumpInt() | |
val name = getElementName(i) | |
val type = it.name | |
builder.add(" $name : $type") | |
if (it.elementsCount > 0) | |
builder.addAll(inners.map { " $it" }) | |
} | |
builder.add("}") | |
return builder | |
} | |
return "$name\n" + dumpInt().joinToString("\n") | |
} | |
@Test | |
fun pluggable() { | |
val data = Data("Foo") | |
val stringData = Json.stringify(Data.serializer(), data) | |
val bytesData = ProtoBuf.dump(Data.serializer(), data) | |
println(stringData) | |
println(HexConverter.printHexBinary(bytesData)) | |
val json = Json(JsonConfiguration.Default) | |
requireNotNull(json) | |
} | |
val digest = MessageDigest.getInstance("MD5") | |
class Hasher(val digest: MessageDigest) : ElementValueEncoder() { | |
override fun encodeByte(value: Byte) = digest.update(value) | |
override fun encodeString(value: String) { | |
digest.update(value.toByteArray()) | |
} | |
} | |
fun hash(data: Data): ByteArray { | |
Data.serializer().serialize(Hasher(digest), data) | |
return digest.digest() | |
} | |
} | |
class StatementSerializer(val stmt: PreparedStatement) : ElementValueEncoder() { | |
override fun encodeString(value: String) { | |
stmt.setString(i++, value) | |
} | |
private var i = 0 | |
} | |
class ResultSetDeserializer(private val rs: ResultSet) : NamedValueDecoder() { | |
override fun decodeTaggedString(tag: String): String { | |
return rs.getString(tag) | |
} | |
} | |
@Serializable | |
data class Color(val r: Byte, val g: Byte, val b: Byte) // #00FFAA | |
const val color = "#00FFAA" | |
@Serializable | |
data class Color(val r: Byte, val g: Byte, val b: Byte) // #00FFAA | |
const val color = "#00FFAA" | |
fun Byte.toHexCode(): String = HexConverter.printHexBinary(byteArrayOf(this)) | |
object SingleColorSerializer : KSerializer<Color> { | |
override fun serialize(encoder: Encoder, obj: Color) { | |
val code = obj.r.toHexCode() + | |
obj.b.toHexCode() + | |
obj.g.toHexCode() | |
encoder.encodeString("#$code") | |
} | |
override fun deserialize(decoder: Decoder): Color { | |
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. | |
} | |
override val descriptor: SerialDescriptor | |
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. | |
} | |
@Serializable | |
data class SingleColor(val argb: Int) | |
const val input = """{"red": 255, "green": 128, "blue": 0}""" | |
object MultipleColorSerializer : KSerializer<SingleColor> { | |
override fun deserialize(decoder: Decoder): SingleColor { | |
var result = 0 | |
val decoder = decoder.beginStructure(descriptor) | |
loop@ while (true) { | |
when (val i = decoder.decodeElementIndex(descriptor)) { | |
0 -> result += decoder.decodeByteElement(descriptor, i).toInt() shl 16 | |
1 -> result += decoder.decodeByteElement(descriptor, i).toInt() shl 8 | |
2 -> result += decoder.decodeByteElement(descriptor, i).toInt() shl 0 | |
READ_DONE -> break@loop | |
} | |
} | |
decoder.endStructure(descriptor) | |
return SingleColor(result) | |
} | |
override fun serialize(encoder: Encoder, obj: SingleColor) { | |
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. | |
} | |
override val descriptor: SerialDescriptor | |
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment