Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide an API to build serial descriptors for recursive data-structures. #2915

Open
qwwdfsad opened this issue Jan 29, 2025 · 3 comments
Open
Labels

Comments

@qwwdfsad
Copy link
Collaborator

Consider the following class:

data class Node(val data: Int, val nodes: List<Node>)

For that, kotlinx.serialization is able to generate a proper serializer (using the coupling between auto-generated serializer and childSerializers loophole), but there is no public API to write it an external (or custom) serial descriptor/serializer for that.

When solving the similar problem for JsonElement serialization, we ended up with the following:

private fun defer(deferred: () -> SerialDescriptor): SerialDescriptor = object : SerialDescriptor {
private val original: SerialDescriptor by lazy(deferred)
override val serialName: String
get() = original.serialName
override val kind: SerialKind
get() = original.kind
override val elementsCount: Int
get() = original.elementsCount
override fun getElementName(index: Int): String = original.getElementName(index)
override fun getElementIndex(name: String): Int = original.getElementIndex(name)
override fun getElementAnnotations(index: Int): List<Annotation> = original.getElementAnnotations(index)
override fun getElementDescriptor(index: Int): SerialDescriptor = original.getElementDescriptor(index)
override fun isElementOptional(index: Int): Boolean = original.isElementOptional(index)
}

Potentially, we can streamline it, pick a decent name (e.g.: recursive, deferred, nested) and make it public

@pdvrieze
Copy link
Contributor

In addition it would be good to make sure that the library (and all formats) work correctly with user provided serial descriptor implementations.

In terms of the general solution, it would be good if the solution could be properly self-referential (rather than having lazily generated copies). This may be mainly the documentation of preferring Type::serializer().serialDescriptor.

@qwwdfsad
Copy link
Collaborator Author

Not sure I follow here, could you please elaborate on the idea?

In addition it would be good to make sure that the library (and all formats) work correctly with user provided serial descriptor implementations.

This part, I think, we'll be extensively covered by tests (and the whole approach is more or less battle-tested with JsonElementSerializer).

it would be good if the solution could be properly self-referential (rather than having lazily generated copies)

The caveat here is that all intermediate serializers have to be aware of that.
I.e. the caveat here is that in the following:

// In node serializer class

buildSerialDescriptor {
     element<List<Node>>(...)
}

it is s List serializer that has to be aware its type argument serializer should be retrieved lazily, not our machinery. The same goes for any other parametrized serializer

@pdvrieze
Copy link
Contributor

I suspect it works already (due to JsonElementSerializer), but some other cases special case specific serializers (AbstractPolymorphicSerializer). In any case, I would see the self-referential aspect as mainly an optimization, hence the idea of just documenting it. I would say that any attempt at caching/lookups would be hard and certainly not worth it if possible at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants