Describe a scenario where using a custom serializer with kotlinx.serialization would be beneficial, and provide a code example illustrating its implementation.

Android interview question for Advanced practice.

Answer

A custom serializer is beneficial when you have a complex data structure that doesn't directly map to the standard Kotlin types handled by kotlinx.serialization, or if you need to handle specific data transformations during serialization or deserialization. For example, let's say you have a data class representing a date and time that needs to be serialized in a specific ISO 8601 format. A custom serializer can handle this: kotlin data class DateTime(val date: LocalDate, val time: LocalTime) val json = Json { serializersModule = SerializersModule { polymorphic<DateTime { subclass(DateTime::class, DateTimeSerializer()) } } } object DateTimeSerializer : KSerializer<DateTime { override val descriptor: SerialDescriptor = buildClassSerialDescriptor("DateTime") { element<LocalDate("date") element<LocalTime("time") } override fun serialize(encoder: Encoder, value: DateTime) { val compositeOutput = encoder.beginStructure(descriptor) compositeOutput.encodeStringElement(descriptor, 0, value.date.toString()) compositeOutput.encodeStringElement(descriptor, 1, value.time.toString()) compositeOutput.endStructure(descriptor) } override fun deserialize(decoder: Decoder): DateTime { val compositeInput = decoder.beginStructure(descriptor) var date: LocalDate? = null var time: LocalTime? = null loop@ while (true) { when (val index = compositeInput.decodeElementIndex(descriptor)) { 0 - date = LocalDate.parse(compositeInput.decodeStringElement(descriptor, index)) 1 - time = LocalTime.parse(compositeInput.decodeStringElement(descriptor, index)) DECODEDONE - break@loop } } compositeInput.endStructure(descriptor) return DateTime(requireNotNull(date), requireNotNull(time)) } }

Explanation

Custom serializers allow you to tailor the serialization/deserialization process to your exact needs.

Related Questions