From a18aed6c54967a1febc74752658e0803b2b9b8c1 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 21 Mar 2019 12:25:23 +0800 Subject: [PATCH] refactor --- gradle.properties | 8 +- .../SauceNaoAPI/SauceNaoAPI.kt | 5 +- .../SauceNaoAPI/models/Header.kt | 29 +++- .../SauceNaoAPI/models/HeaderIndex.kt | 14 ++ .../models/{Index.kt => Result.kt} | 8 +- .../SauceNaoAPI/models/SauceNaoAnswer.kt | 2 +- .../SauceNaoAPI/utils/JSONSerializers.kt | 162 ++++++++++++++++++ 7 files changed, 213 insertions(+), 15 deletions(-) create mode 100644 src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/HeaderIndex.kt rename src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/{Index.kt => Result.kt} (53%) create mode 100644 src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/utils/JSONSerializers.kt diff --git a/gradle.properties b/gradle.properties index 209f039..ca94a12 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ kotlin.code.style=official -kotlin_version=1.3.11 -kotlin_coroutines_version=1.1.0 -kotlin_serialisation_runtime_version=0.9.1 +kotlin_version=1.3.21 +kotlin_coroutines_version=1.1.1 +kotlin_serialisation_runtime_version=0.10.0 joda_time_version=2.10.1 -ktor_version=1.1.1 +ktor_version=1.1.2 gradle_bintray_plugin_version=1.8.4 diff --git a/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/SauceNaoAPI.kt b/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/SauceNaoAPI.kt index c976d16..82a0b98 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/SauceNaoAPI.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/SauceNaoAPI.kt @@ -1,5 +1,6 @@ package com.github.insanusmokrassar.SauceNaoAPI +import com.github.insanusmokrassar.SauceNaoAPI.models.Result import com.github.insanusmokrassar.SauceNaoAPI.models.SauceNaoAnswer import io.ktor.client.HttpClient import io.ktor.client.call.call @@ -7,7 +8,7 @@ import io.ktor.client.engine.okhttp.OkHttp import io.ktor.client.request.parameter import io.ktor.client.request.url import io.ktor.client.response.readText -import kotlinx.serialization.json.JSON +import kotlinx.serialization.json.Json private const val API_TOKEN_FIELD = "api_key" private const val OUTPUT_TYPE_FIELD = "output_type" @@ -91,7 +92,7 @@ data class SauceNaoAPI( resultsCount ?.also { parameter(RESULTS_COUNT_FIELD, it) } minSimilarity ?.also { parameter(MINIMAL_SIMILARITY_FIELD, it) } }.response.readText().let { - JSON.nonstrict.parse( + Json.nonstrict.parse( SauceNaoAnswer.serializer(), it ) diff --git a/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/Header.kt b/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/Header.kt index 8db0049..d3e1782 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/Header.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/Header.kt @@ -1,7 +1,9 @@ package com.github.insanusmokrassar.SauceNaoAPI.models -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable +import com.github.insanusmokrassar.SauceNaoAPI.utils.JsonObjectSerializer +import kotlinx.serialization.* +import kotlinx.serialization.internal.* +import kotlinx.serialization.json.* @Serializable data class Header( @@ -21,8 +23,9 @@ data class Header( val status: Int, @SerialName("results_requested") val resultsRequested: Int, + @Serializable(IndexesSerializer::class) @SerialName("index") - val indexes: List, + val indexes: List, @SerialName("search_depth") val searchDepth: Int, @SerialName("minimum_similarity") @@ -34,3 +37,23 @@ data class Header( @SerialName("results_returned") val resultsCount: Int ) + +object IndexesSerializer : KSerializer> { + override val descriptor: SerialDescriptor = StringDescriptor + + override fun deserialize(decoder: Decoder): List { + val json = decoder.decodeSerializableValue(JsonObjectSerializer) + val parsed = json.keys.mapNotNull { it.toIntOrNull() }.sorted().mapNotNull { + val jsonObject = json.getObjectOrNull(it.toString()) ?: return@mapNotNull null + val index = Json.nonstrict.parse(HeaderIndex.serializer(), Json.stringify(JsonObjectSerializer, jsonObject)) + it to index + }.toMap() + return Array(parsed.keys.max() ?: 0) { + parsed[it] + }.toList() + } + + override fun serialize(encoder: Encoder, obj: List) { + TODO() + } +} diff --git a/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/HeaderIndex.kt b/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/HeaderIndex.kt new file mode 100644 index 0000000..e1efa2b --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/HeaderIndex.kt @@ -0,0 +1,14 @@ +package com.github.insanusmokrassar.SauceNaoAPI.models + +import kotlinx.serialization.Optional +import kotlinx.serialization.Serializable + +@Serializable +data class HeaderIndex( + val status: Int, + val id: Int, + @Optional + val results: Int = 0, + @Optional + val parent_id: Int? = null +) diff --git a/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/Index.kt b/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/Result.kt similarity index 53% rename from src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/Index.kt rename to src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/Result.kt index f6080d0..b2a1c93 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/Index.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/Result.kt @@ -3,9 +3,7 @@ package com.github.insanusmokrassar.SauceNaoAPI.models import kotlinx.serialization.Serializable @Serializable -data class Index( - val status: Int, - val parent_id: Int, - val id: Int, - val results: Int +data class Result( + val header: ResultHeader, + val data: ResultData ) diff --git a/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/SauceNaoAnswer.kt b/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/SauceNaoAnswer.kt index 9834cb7..2fee63f 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/SauceNaoAnswer.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/models/SauceNaoAnswer.kt @@ -5,5 +5,5 @@ import kotlinx.serialization.Serializable @Serializable data class SauceNaoAnswer( val header: Header, - val data: ResultData + val results: List ) diff --git a/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/utils/JSONSerializers.kt b/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/utils/JSONSerializers.kt new file mode 100644 index 0000000..5730bed --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/SauceNaoAPI/utils/JSONSerializers.kt @@ -0,0 +1,162 @@ +package com.github.insanusmokrassar.SauceNaoAPI.utils + +import kotlinx.serialization.* +import kotlinx.serialization.internal.* +import kotlinx.serialization.json.* + + +@Serializer(forClass = JsonElement::class) +internal object JsonElementSerializer : KSerializer { + override val descriptor: SerialDescriptor = object : SerialClassDescImpl("JsonElementSerializer") { + override val kind: SerialKind + get() = UnionKind.SEALED + + init { + addElement("JsonElement") + } + } + + override fun serialize(encoder: Encoder, obj: JsonElement) { + when (obj) { + is JsonPrimitive -> JsonPrimitiveSerializer.serialize(encoder, obj) + is JsonObject -> JsonObjectSerializer.serialize(encoder, obj) + is JsonArray -> JsonArraySerializer.serialize(encoder, obj) + } + } + + override fun deserialize(decoder: Decoder): JsonElement { + val input = decoder as? JsonInput ?: error("JsonElement is deserializable only when used by Json") + return input.decodeJson() + } +} + +@Serializer(forClass = JsonPrimitive::class) +internal object JsonPrimitiveSerializer : KSerializer { + override val descriptor: SerialDescriptor = + JsonPrimitiveDescriptor + + override fun serialize(encoder: Encoder, obj: JsonPrimitive) { + return if (obj is JsonNull) { + JsonNullSerializer.serialize(encoder, JsonNull) + } else { + JsonLiteralSerializer.serialize(encoder, obj as JsonLiteral) + } + } + + override fun deserialize(decoder: Decoder): JsonPrimitive { + return if (decoder.decodeNotNullMark()) JsonPrimitive(decoder.decodeString()) + else JsonNullSerializer.deserialize(decoder) + } + + private object JsonPrimitiveDescriptor : SerialClassDescImpl("JsonPrimitive") { + override val kind: SerialKind + get() = PrimitiveKind.STRING + + override val isNullable: Boolean + get() = true + + init { + JsonPrimitiveSerializer.JsonPrimitiveDescriptor.addElement("JsonPrimitive") + } + } +} + +@Serializer(forClass = JsonNull::class) +internal object JsonNullSerializer : KSerializer { + override val descriptor: SerialDescriptor = + JsonNullDescriptor + + override fun serialize(encoder: Encoder, obj: JsonNull) { + encoder.encodeNull() + } + + override fun deserialize(decoder: Decoder): JsonNull { + decoder.decodeNull() + return JsonNull + } + + private object JsonNullDescriptor : SerialClassDescImpl("JsonNull") { + override val kind: SerialKind + get() = UnionKind.OBJECT + + override val isNullable: Boolean + get() = true + + init { + JsonNullSerializer.JsonNullDescriptor.addElement("JsonNull") + } + } +} + +@Serializer(forClass = JsonLiteral::class) +internal object JsonLiteralSerializer : KSerializer { + + override val descriptor: SerialDescriptor = + JsonLiteralDescriptor + + override fun serialize(encoder: Encoder, obj: JsonLiteral) { + if (obj.isString) { + return encoder.encodeString(obj.content) + } + + val integer = obj.intOrNull + if (integer != null) { + return encoder.encodeInt(integer) + } + + val double = obj.doubleOrNull + if (double != null) { + return encoder.encodeDouble(double) + } + + val boolean = obj.booleanOrNull + if (boolean != null) { + return encoder.encodeBoolean(boolean) + } + + encoder.encodeString(obj.content) + } + + override fun deserialize(decoder: Decoder): JsonLiteral { + return JsonLiteral(decoder.decodeString()) + } + + private object JsonLiteralDescriptor : SerialClassDescImpl("JsonLiteral") { + override val kind: SerialKind + get() = PrimitiveKind.STRING + + init { + JsonLiteralSerializer.JsonLiteralDescriptor.addElement("JsonLiteral") + } + } +} + +@Serializer(forClass = JsonObject::class) +internal object JsonObjectSerializer : KSerializer { + override val descriptor: SerialDescriptor = + NamedMapClassDescriptor("JsonObject", StringSerializer.descriptor, + JsonElementSerializer.descriptor) + + override fun serialize(encoder: Encoder, obj: JsonObject) { + LinkedHashMapSerializer(StringSerializer, JsonElementSerializer).serialize(encoder, obj.content) + } + + override fun deserialize(decoder: Decoder): JsonObject { + return JsonObject(LinkedHashMapSerializer(StringSerializer, JsonElementSerializer).deserialize(decoder)) + } +} + +@Serializer(forClass = JsonArray::class) +internal object JsonArraySerializer : KSerializer { + + override val descriptor: SerialDescriptor = NamedListClassDescriptor("JsonArray", + JsonElementSerializer.descriptor) + + override fun serialize(encoder: Encoder, obj: JsonArray) { + ArrayListSerializer(JsonElementSerializer).serialize(encoder, obj) + } + + override fun deserialize(decoder: Decoder): JsonArray { + return JsonArray(ArrayListSerializer(JsonElementSerializer).deserialize(decoder)) + } +}