kotlinx.serialization icon indicating copy to clipboard operation
kotlinx.serialization copied to clipboard

Contextually register in SerializersModule nullable type

Open laurynas-agmis opened this issue 3 years ago • 5 comments

Registering serializer module as contextual and passing a KSerializer that has nullable type is not allowed. It is needed due to bad backend responses when field value is not null, but empty string instead.

Example: val module = SerializersModule { contextual(serializer = LocalDateSerializer) -> shows error Type mismatch. Required: Any Found: LocalDate? }

LocalDateSerializer class @Serializer(forClass = LocalDate::class) object LocalDateSerializer : KSerializer<LocalDate?> { private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDate?", PrimitiveKind.STRING).nullable

override fun serialize(encoder: Encoder, value: LocalDate?) {
    encoder.encodeString(formatter.format(value))
}

override fun deserialize(decoder: Decoder): LocalDate? {
    val output = decoder.decodeString()
    val result = try {
        LocalDate.from(formatter.parse(output))
    } catch (e: DateTimeParseException) {
        ZonedDateTime.parse(output).toLocalDate()
    } catch (e: Exception) {
       null
    }
    return result
}

}

laurynas-agmis avatar Jan 12 '22 08:01 laurynas-agmis

The problem is in KClass<T> that requires T to be non-nullable and inferred in this overload automatically. However, I think it may be possible to change the signature of contextual(kClass: KClass<T>, serializer: KSerializer<T>) overload.

sandwwraith avatar Jan 18 '22 13:01 sandwwraith

Is there a workaround? I'm currently stuck on this, I'm surprised it's not possible to serialize nullable values.

CLOVIS-AI avatar Sep 04 '22 13:09 CLOVIS-AI

The same problem here, registering nullable string serializer like this:

object NullableStringSerializer : KSerializer<String?> {
    private val delegate = serializer<String>().nullable
    override val descriptor: SerialDescriptor = delegate.descriptor

    override fun serialize(encoder: Encoder, value: String?) {
        delegate.serialize(encoder, value?.ifEmpty { null })
    }

    override fun deserialize(decoder: Decoder): String? {
        return delegate.deserialize(decoder)?.ifEmpty { null }
    }
}

val module = SerializersModule {
    contextual(NullableStringSerializer)
//             ~~~~~~~~~~~~~~~~~~~~~~~~
}

This results in error:

Type mismatch.
Required: Any
Found: String?

Is there any workaround available please?

morki avatar Jan 31 '23 17:01 morki

@morki Maybe contextual(NullableStringSerializer as KSerializer<String>) would work

sandwwraith avatar Jan 31 '23 18:01 sandwwraith