Daniel Müller
Daniel Müller

Reputation: 411

Serialize Kotlin nested classes to flat JSON

I'm looking for a standardized way to serialize a Kotlin object into a "flat" Json with only key -> value pairs using kotlinx.serialization on the JVM.

An Example:

@Serializable
data class Address(val street: String, val postalCode: String)

@Serializable
data class Customer(val id: String, val name: String, val address: Address)

The default behaviour upon serialization is:

{
    "id": "123ABC",
    "name": "Joe"
    "address": {
        "street": "my street",
        "postalCode": "123456"
    }
}

What i want is:

{
    "id": "123ABC",
    "name": "Joe"
    "street": "my street",
    "postalCode": "123456"
}

I could not find a way to accomplish this in the Kotlin Serialization Guide. Therefore I'm quite sure I have to implement a custom KSerializer<Customer> but currently don't see how to achieve to behaviour.

Upvotes: 4

Views: 3859

Answers (1)

This could be done with surrogate serializer technique:

import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.*
import kotlinx.serialization.*

@Serializable
data class Address(val street: String, val postalCode: String)

@Serializable(with = CustomerSerializer::class)
data class Customer(val id: String, val name: String, val address: Address)

@Serializable
@SerialName("Customer")
private data class CustomerSurrogate(val id: String, val name: String, val street: String, val postalCode: String)

object CustomerSerializer : KSerializer<Customer> {
    override val descriptor: SerialDescriptor = CustomerSurrogate.serializer().descriptor
    override fun deserialize(decoder: Decoder): Customer {
        val surrogate = decoder.decodeSerializableValue(CustomerSurrogate.serializer())
        return Customer(surrogate.id, surrogate.name, Address(surrogate.street, surrogate.postalCode))
    }

    override fun serialize(encoder: Encoder, value: Customer) {
        val surrogate = CustomerSurrogate(value.id, value.name, value.address.street, value.address.postalCode)
        encoder.encodeSerializableValue(CustomerSurrogate.serializer(), surrogate)
    }
}

fun main() {
    println(Json.encodeToString(Customer("123ABC", "Joe", Address("my street", "123456"))))
}

Upvotes: 7

Related Questions