3

I'm using kotlinx-serialization-json

I have this class:

@Serializable
data class Event(
    @SerialName("temperature") val temperature: Float?,
    @SerialName("pressure") val pressure: Float?,
    @SerialName("humidity") val humidity: Float?,
)

and this call

Json.encodeToString(Event(temperature = 42.0f, pressure = null, humidity = 20.9f))

During serialization I receive such json:

{
    "temperature": 20.5,
    "pressure": null,
    "humidity": 20.9
}

but I would like to prevent serializing null values and receive this:

{
    "temperature": 20.5,
    "humidity": 20.9
}

It's a problem for me, because during serializing lengthy list of events I waste a lot of lines. Anyone's got an idea how to achieve this?

EDIT:

There is new simple way to achieve this: https://blog.jetbrains.com/kotlin/2021/09/kotlinx-serialization-1-3-released/#excluding-nulls

1 Answer 1

4

You can ignore all defaults and do something like this:

@Serializable
data class Event(
    @SerialName("temperature") val temperature: Float?,
    @SerialName("pressure") val pressure: Float? = null,
    @SerialName("humidity") val humidity: Float?,
)

    val jsonMapper = Json { encodeDefaults = false}
    val body = jsonMapper.encodeToString(Event(temperature = 42.0f,pressure = null, humidity = 20.9f))

Please, be aware of that, in the above case you are ignoring ALL defaults. If you want to ignore only null values you have to implement a custom serializer.

For this example custom serializer will look like this:


object EventSerializer: KSerializer<Event> {
    override fun deserialize(decoder: Decoder): Event {
        decoder.decodeStructure(descriptor) {
            var temperature: Float? = null
            var humidity:Float? = null
            var pressure: Float? = null

            while (true) {
                when (val index = decodeElementIndex(descriptor)) {
                    0 -> temperature = decodeFloatElement(descriptor, 0)
                    1 -> pressure = decodeFloatElement(descriptor, 1)
                    2 -> humidity = decodeFloatElement(descriptor, 2)
                    CompositeDecoder.DECODE_DONE -> break
                    else -> error("Unexpected index: $index")
                }
            }
           return Event(temperature, pressure, humidity)
        }
    }

    override fun serialize(encoder: Encoder, value: Event) {
        encoder.beginStructure(descriptor).run {
            value.temperature?.let { encodeStringElement(descriptor, 0, it.toString()) }
            value.pressure?.let { encodeStringElement(descriptor, 1, it.toString()) }
            value.humidity?.let { encodeStringElement(descriptor, 2, it.toString()) }
            endStructure(descriptor)
        }
    }

    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Event") {
        element<String>("temperature")
        element<String>("pressure")
        element<String>("humidity")

    }
}

To use it -> @Serializable(with = EventSerializer::class)

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, maybe I'll decide to go with encodeDefaults = false as a project-level design decision
val json = Json { encodeDefaults = true explicitNulls = false }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.