Hello! I’m trying to manually parse a `Map<Stri...
# serialization
g
Hello! I’m trying to manually parse a
Map<String, String>
json response from github’s API into a
List<Object>
but I’m failing to do so, more in the thread
Copy code
@Serializable
data class Emoji(val name: String, val imageUrl: String)
Copy code
@OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
object EmojiListSerializer : KSerializer<List<Emoji>> {

    override val descriptor: SerialDescriptor = mapSerialDescriptor<String, String>()

    override fun deserialize(decoder: Decoder): List<Emoji> {
        val list = mutableListOf<Emoji>()
        decoder.decodeStructure(descriptor) {
            loop@ while (true) {
                when (val index = decodeElementIndex(descriptor)) {
                    DECODE_DONE -> break@loop
                    else -> list.add(decodeSerializableElement(descriptor, index, Emoji::class.serializer()))
                }
            }
        }
        return list
    }

    override fun serialize(encoder: Encoder, value: List<Emoji>) {
        error("Serialization is not supported")
    }
}
Copy code
@Provides
@Singleton
@RetrofitForEmojiList
@Suppress("JSON_FORMAT_REDUNDANT")
fun provideCustomRetrofit(client: OkHttpClient, @ApiUrl url: String): Retrofit {
    return Retrofit.Builder()
        .client(client)
        .baseUrl(url)
        .addConverterFactory(
            Json {
                serializersModule = SerializersModule { contextual(EmojiListSerializer) }
            }.asConverterFactory("application/json".toMediaType())
        )
        .build()
}
I’m using
com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
but the KSerializer is not called 🤷
and when I call
Copy code
@GET(EMOJI)
suspend fun emojiList(): Response<Map<String, String>>
if I put a break point in the
KSerializer
deserialize
method, is not called
what am I missing?
note: I don’t need to use a KSerializer to map this response, am doing it just as an exercise
d
You just answered your own question at the end there. The serialiser is not needed so it's not called.
g
hum… so if it was something like
Map<String, Any>
it would?
d
Nope it wouldn't get called. You'll need to return a type that has been marked with your custom serialiser.
👍 1
p
Why not just mapping the map instead of handling it in the serializer? We treat that networking layer as dumb and just parse things by hand
g
that’s what am i doing 🙂 just wanted to try the
KSerializer
with a network response like I would do with moshi:
Copy code
class EmojiAdapter : JsonAdapter<List<Emoji>>(), JsonAdapter.Factory {

    override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<List<Emoji>>? {
        if (type == Types.newParameterizedType(List::class.java, Emoji::class.java)) {
            return this@EmojiAdapter
        }
        return null
    }

    @Throws(IOException::class)
    override fun fromJson(reader: JsonReader): List<Emoji> {
        val result = ArrayList<Emoji>()
        reader.beginObject()
        while (reader.hasNext()) {
            result.add(Emoji(reader.nextName(), reader.nextString()))
        }
        reader.endObject()
        return result
    }

    @Throws(IOException::class)
    override fun toJson(writer: JsonWriter, value: List<Emoji>?) {
        //not used
    }
}
p
I've given up an all that - just mapping in an encapsulated layer is so much easier to write and to maintain
👍 1
g
that’s true
p
And usually when it's a network call that is so much more expensive than mapping that it doesn't matter if there is a map allocated