I've a question: I've an api that returns informat...
# serialization
t
I've a question: I've an api that returns information for the pagination and the list of items in the same object:
Copy code
{
     "offset":0,
     "limit":15,
     "count":1,
     //and a field with custom fieldname with the items
     "fooItems": [...]
}
Can I transform this somehow in a class like this:
Copy code
class PaginationResult<T>(
    val offset : Int,
    val limit : Int,
    val count : Int,
    val items : List<T>
}
@Serializable FooItem
@serializable BarItem
Just add the annotation to PaginationResult and it should work
t
it does work, but the problem is that "fooItems" is changing every call. This means that I have to declare a result with those fields every time. The thing I'm basically looking for is one that transforms the last field to items, such that I can really use the PaginationResult. I tried this, but it is not working at this moment:
Copy code
@Serializable(ListResponseSerializer::class)
class ListResponse<T>(
    val offset: Int,
    val limit: Int,
    val count: Int,
    val totalCount: Int,
    val items : List<T>
)

class ListResponseSerializer<T>(
    private val dataSerializer : KSerializer<T>
) : KSerializer<ListResponse<T>>{
    override fun deserialize(
        decoder: Decoder
    ): ListResponse<T> {
        val jsonInput = decoder as JsonDecoder ?: error("only json supported")
        val json = jsonInput.decodeJsonElement().jsonObject
        val element = json.keys - arrayOf("offset", "limit", "count", "totalCount")
        val elementKey = element.single()
        val i = json.keys.indexOf(elementKey)
        val listSerializable = ListSerializer(dataSerializer)
        return ListResponse(
            offset = json["offset"]!!.<http://jsonPrimitive.int|jsonPrimitive.int>,
            limit = json["limit"]!!.<http://jsonPrimitive.int|jsonPrimitive.int>,
            count = json["count"]!!.<http://jsonPrimitive.int|jsonPrimitive.int>,
            totalCount = json["totalCount"]!!.<http://jsonPrimitive.int|jsonPrimitive.int>,
            items = decoder.decodeSerializableElement(listSerializable.descriptor, i, listSerializable)
        )
    }

    override val descriptor = buildClassSerialDescriptor("ListResponse"){
        element<Int>("offset")
        element<Int>("limit")
        element<Int>("count")
        element<Int>("totalCount")
        element("items", dataSerializer.descriptor)
    }

    override fun serialize(encoder: Encoder, value: ListResponse<T>) : Nothing=TODO()
}
j
foo items is polymorphic? How many different items you have?
Maybe you should use a sealed class for T for example
t
Do you mean there is no simple way to transform the unmatched field to become items? In one call I get
fooItems
and in another call its
barItems
. I wanted to rename those changing field to
items
and then link them with a generic type. During retrieval, I am always aware of the real type, so the generic itself should always work. The result would be that I have one generic data-class that knows about pagination. All my other data-classes just know about their own data. Also, I would keep it generic, which means that the items which I'm retrieving can be completely independent and they don't have to be declared in a serialzarsmodule.
Copy code
@Serializable
class ResultA(
    val id : Int,
    val name : String
)

fun test(){
    val result1 = """{
    "offset":0,
    "limit":15,
    "count":15,
    "totalCount":"54",
    "aList": [
        {"id":1,"name":"foo"},
        {"id":2,"name":"bar"},
        {"id":3,"name":"foobar"}
    ]
}"""
    Json.decodeFromString<ListResponse<ResultA>>(result1)
}

@Serializable
class ResultB(
    val id : Int,
    val age : Int,
)
@Test
fun test2(){
    val result2 = """{
    "offset":0,
    "limit":15,
    "count":15,
    "totalCount":"54",
    "bList": [
        {"id":1,"name":10},
        {"id":2,"name":20},
        {"id":3,"name":30}
    ]
}"""
    Json.decodeFromString<ListResponse<ResultB>>(result2)
}

@Serializable(ListResponseSerializer::class)
class ListResponse<T>(
    val offset: Int,
    val limit: Int,
    val count: Int,
    val totalCount: Int,
    val items : List<T>
)
j
Ah, I misunderstood you, sorry
t
Haha, if you misunderstood, it meaned I wasn't clear enough 😉
🙂 1
Ah, was mistreating decoder. this works:
Copy code
class ListResponseSerializer<T>(
    private val dataSerializer : KSerializer<T>
) : KSerializer<ListResponse<T>>{
    override fun deserialize(decoder: Decoder): ListResponse<T> {
        val jsonInput = decoder as? JsonDecoder ?: error("only json supported")
        val json = jsonInput.decodeJsonElement().jsonObject
        val element = json.keys - arrayOf("offset", "limit", "count", "totalCount")
        val elementKey = element.single()
        val value = json[elementKey]
        return ListResponse(
            offset = json["offset"]!!.<http://jsonPrimitive.int|jsonPrimitive.int>,
            limit = json["limit"]!!.<http://jsonPrimitive.int|jsonPrimitive.int>,
            count = json["count"]!!.<http://jsonPrimitive.int|jsonPrimitive.int>,
            totalCount = json["totalCount"]!!.<http://jsonPrimitive.int|jsonPrimitive.int>,
            items = Json.decodeFromJsonElement(ListSerializer(dataSerializer), value!!)
        )
    }

    override val descriptor = buildClassSerialDescriptor("ListResponse"){
        element<Int>("offset")
        element<Int>("limit")
        element<Int>("count")
        element<Int>("totalCount")
        element("items", listSerialDescriptor(dataSerializer.descriptor))
    }

    override fun serialize(encoder: Encoder, value: ListResponse<T>) : Nothing=TODO()
}
👍 1
Thanks for your attention.
👍 2