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

Make accessible the original descriptor when wrapped into nullable

Open Chuckame opened this issue 1 year ago • 3 comments

What is your use-case and why do you need this feature? I'm currently working for the avro4k library that is the implementation of avro format with this powerful kotlin serialization library.

We need to generate schemas usually more complex than just based on descriptors' kinds. To do that, it was previously done by creating a custom class implementing a SerialDescriptor and add custom methods to generate the schemas according to a custom logic. The main issue with it is that, just for nullable descriptor (with descriptor.nullable done by the plugin), with need to use kotlin reflection to get back the original descriptor because it is a private field. To bypass it, we had to make a complex system that discovers custom annotations and generate a schema according those annotations.

But still, the simplest way would be to just access to the original descriptor. It allows us to decouple the schema generation system from the custom logics inferred by the custom serializers.

Here is the example of what

Describe the solution you'd like Just make public the original field inside the SerialDescriptorForNullable data class.

A second option would be to have the same extension method as public val SerialDescriptor.capturedKClass: KClass<*>?:

public val SerialDescriptor.unwrapOriginalDescriptor: SerialDescriptor
    get() = when (this) {
        is SerialDescriptorForNullable -> this.original
        else -> this
    }

I can do the PR if needed

Chuckame avatar Apr 15 '24 20:04 Chuckame

Yes, I think this makes sense, even though we do not encourage implementing SerialDescriptor manually (but we don't prohibit it either).

sandwwraith avatar Apr 17 '24 10:04 sandwwraith

To be honest, I don't really like doing this, but I don't have a better idea without doing any reflection, and keep the serializer's and the schema generation together... Since the schema is highly linked to the serializer's descriptor, so when we want to generate a custom schema, then it makes sense to have it close to the descriptor.

When do you think the PR would be merged and released? Do you have a weekly, monthly or quartly releases?

To bypass this, currently, I use buildSerialDescriptor to add a custom annotation providing the object's class of the schema supplier. So the pro is that we can link the schema directly on the descriptor, and the cons is that we need reflection to get the supplier's instance...

Why buildSerialDescriptor is an international api as it allows us to put additional metadata using annotations?

Chuckame avatar Apr 17 '24 11:04 Chuckame

@Chuckame In the development branch of xmlutil I have a similar situation. I made a subtype of KSerializer that would allow for special handling in the format (making the format aware that the serializer treats xml special), this allows for not having to special case certain types (like dom nodes/elements).

These special serializers may use a different descriptor for xml than for the normal serialization flow. This is implemented through a special annotation as marker, and when this annotation is present, the format will then use a negative index to elementSerialDescriptor to get the format specific descriptor. This works because NullableDescriptor doesn't check indices, but directly forwards the index. I would have preferred to just subtype SerialDescriptor and "extract" it from the nullable descriptor. See: https://github.com/pdvrieze/xmlutil/blob/e8af41966b52be72d91cb1cb1eb510f5a3630b04/core/base/src/commonMain/kotlin/nl/adaptivity/xmlutil/XmlSerializer.kt#L44-L50

As an aside, the XML library also uses deep type inspection to construct the structure (before serializing/deserializing it creates a format specific descriptor that actually directs the structure - this is configurable using a policy), you may want to have a look. ( https://github.com/pdvrieze/xmlutil/blob/master/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/structure/XmlDescriptor.kt ). The sharing of the logic between serialization and deserialization also helps with keeping this consistent.

pdvrieze avatar Apr 18 '24 08:04 pdvrieze