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

Allow calling `SerialDescriptor(name, original)` when `original.kind` is `PrimitiveKind`

Open lukellmann opened this issue 1 year ago • 5 comments

What is your use-case and why do you need this feature?

This section of the Kotlin Serialization Guide recommends that delegating serializers should not reuse the descriptor of the serializer they are delegating to but instead wrap it with SerialDescriptor("OtherName", delegate.descriptor). This works fine if delegate.descriptor.kind is not PrimitiveKind. But for serializers that have a primitive descriptor, this will throw an exception.

This can cause problems if the delegated serializer just happens to have a primitive serial descriptor as an implementation detail.

An example for this is this serializer which delegates to JsonPrimitive.serializer(). However when writing this serializer, one shouldn't have to know what the serial descriptor for JsonPrimitive looks like if all that one wants to do is delegate serialization to JsonPrimitive.serializer(). Ideally you should always be able to call SerialDescriptor("OtherName", delegate.descriptor) to implement KSerializer.descriptor for a serializer that just delegates to delegate.

Describe the solution you'd like

Change the code for SerialDescriptor() to not throw when given a primitive descriptor. This could be done by simply delegating to PrimitiveSerialDescriptor():

public fun SerialDescriptor(serialName: String, original: SerialDescriptor): SerialDescriptor {
    require(serialName.isNotBlank()) { "..." }
    require(serialName != original.serialName) { "..." }

    val kind = original.kind
    return if (kind is PrimitiveKind) {
        PrimitiveSerialDescriptor(serialName, kind)
    } else {
        WrappedSerialDescriptor(serialName, original)
    }
}

lukellmann avatar Jan 16 '24 19:01 lukellmann

I'd be happy to implement this if it's accepted.

lukellmann avatar Jan 26 '24 12:01 lukellmann

Yes, I think this is logical and will make writing delegating serializers easier. I'll be happy to receive your PR!

sandwwraith avatar Feb 05 '24 18:02 sandwwraith

I stumbled accross the situation where a primitive descriptor is nullable: this won't be copied over to the wrapped serializer by the code shown above. So it isn't as easy as I thought it would be.

lukellmann avatar Feb 06 '24 02:02 lukellmann

You can use .nullable https://github.com/Kotlin/kotlinx.serialization/blob/06aabd22cd2960da1914f9a7ad30873269eb8959/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptors.kt#L220 to wrap it if original.isNullable

sandwwraith avatar Feb 06 '24 10:02 sandwwraith