hello, is it possible to embed the contents of `da...
# serialization
a
hello, is it possible to embed the contents of
data
directly into the root of the
Response
object?
Copy code
@Serializable
data class Response<T>(
  val data: T,
  val field2: String,
  val field3: Int,
)

// so that
@Serializable
data class Data(
  val field1: String,
)

// results in:
{
  field1: string,
  field2: string,
  field3: number
}
g
Why not using
value class
for your "Data"?
a
Data
is simply an example, the actual classes used for T are fairly complex
g
Ok, if there's more than 1 field then, you can use a custom serializer to cover that case (KSerializer or JsonTransformingSerializer I guess).
a
i haven't involved myself with
kotlinx.serialization
beyond simple use cases. how would i setup that deserialization logic in the custom serializer?
given that there are multiple classes that can be in place of T
my use case is that the API returns additional stuff like billing along with the data for each endpoint
g
JsonTransformingSerializer is limited to json but will allow you to get a JsonElement that you can change as you wish. Let me try a draft pseudocode example from memory :
Copy code
class ResponseSerializer(generatedSerializer: KSerializer<T>) : JsonTransformingSerializer<Response<T>>(serializer) {
  private val field2Name = "field2"
  private val field3Name = "field3"
  override val descriptor: SerialDescriptor = buildClassSerialDescriptor {
    element<String>(field2Name)
    element<String>(field3Name)
    val fieldDescriptorIndex = serializer.descriptor.getElementIndex(fieldName)
    val fieldDescriptor = serializer.descriptor.getElementDescriptor(fieldDescriptorIndex)
    fieldDescriptor.elementNames.forEachIndexed { index, name ->
        val content = fieldDescriptor.getElementDescriptor(index)
        element(
            elementName = name,
            annotations = content.annotations,
            descriptor = content,
            isOptional = content.isNullable
        )
    }
  }

    override fun transformSerialize(element: JsonElement): JsonElement {
        val contentJson = requireNotNull(element.jsonObject[fieldName]).jsonObject
        val field2 = requireNotNull(element.jsonObject[field2Name]).jsonPrimitive.content
        val field3 = requireNotNull(element.jsonObject[field3Name]).jsonPrimitive.content
        val updated = buildMap {
            put(rootUUIDFieldName, JsonPrimitive(rootUUID))
            put(playheadFieldName, JsonPrimitive(playhead))
            putAll(contentJson)
        }
        return super.transformSerialize(JsonObject(updated))
    }

    override fun transformDeserialize(element: JsonElement): JsonElement {
        val field2 = requireNotNull(element.jsonObject[field2Name]).jsonPrimitive.content
        val field3 = requireNotNull(element.jsonObject[field3Name]).jsonPrimitive.content
        val updated = element.jsonObject - rootUUIDFieldName
        return super.transformDeserialize(
            JsonObject(
                mapOf(
                    field2Name to JsonPrimitive(rootUUID),
                    field3Name to JsonPrimitive(playhead),
                    fieldName to JsonObject(updated),
                )
            )
        )
    }
}
Basically the idea is : • descriptor defines what the json should look like, so we define field2 & 3 manually, and we get all field1 information from the generatedSerializer of T • the transformation are mapping the KotlinX expected serialization format with what you want to output/intput
Pretty sure there's some example to find in this slack, in the doc or in the github repos
a
hmm, can you tell me how do i obtain the second serializer inside JsonTransformingSerializer?
i should've just rtfd haha
i did end up modifying the original class
Copy code
@Serializable(ResponseSerializer::class)
sealed class Response<out T> {
  data class Ok<out T>(val data: T, val otherFields: String) : Response<T>()
  data class Error(val error: Err) : Response<Nothing>()
}
thanks for looking into my issue! you can really do anything with this serialization framework 🙌
👍 2