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

Improve documentation for buildClassSerialDescriptor

Open cris-null opened this issue 4 years ago • 1 comments

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

I'm trying to build a custom KSerializer for a class that has a list property:

@Serializable(with = ListingSerializer::class)
data class Listing(
    val after: String?,
    val before: String?,
    val posts: List<Post>
)

object ListingSerializer: KSerializer<Listing> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Listing") {
        element<String>("after")
        element<String>("before")
       // what to put in here ??
    }

    override fun serialize(encoder: Encoder, value: Listing) {  ...  }
    override fun deserialize(decoder: Decoder): Listing {  ...  }
}

The hand-written composite serializer section for the serializers guide says to use buildClassSerialDescriptor. However, it does not say that to use when using collections. The guide for json also has a few snippets where buildClassSerialDescriptor is used, but not for collections.

Checking the documentation for buildClassSerialDescriptor itself, I'm quite confused because it doesn't show buildClassSerialDescriptor but instead SerialDescriptor. Am I supposed to use SerialDescriptor instead? That's not what it's in the guide, so I'm a bit confused.

Inside it are these three lines:

    element<Long>("_longField") // longField is named as _longField
    element("stringField", listDescriptor<String>())
    element("nullableInt", descriptor<Int>().nullable)

This seems similar to what the guide says about buildClassSerialDescriptor, but calling them inside it does not work for listDescriptor or descriptor<Int>().nullable.

In the documentation for buildClassSerialDescriptor, there also an example for generic classes. In it, there's listSerialDescriptor which is what I used for my case.

override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Listing") {
    element<String>("after")
    element<String>("before")

    // is this better?
    element("things", listSerialDescriptor(Post.serializer().descriptor))
    // or is this better?
    element("things", listSerialDescriptor<Post>())
}

My code works great! Although it says it's "experimental" so I'm not sure if it's the recommended way to do it.

Describe the solution you'd like

I think it would be pretty cool if the guide had an example for a descriptor using a collection, or if the documentation for buildClassSerialDescriptor looked a bit more like how it's used in the guide (and with a collection example).

Environment

  • Kotlin version: 1.4.32
  • Library version: 1.4.32
  • Kotlin platforms: JVM
  • Gradle version: 6.7
  • IDE version: 2021.1
  • Other relevant context: Java 11, Linux 5.10

cris-null avatar Apr 24 '21 14:04 cris-null

Run into the same problem. Outdated doc is not very optimal... Maybe at least add a warning that this section is outdated?

jschneider avatar Jun 08 '22 19:06 jschneider

SerialDescriptor in the doc of buildClassSerialDescriptor is merely an error, there's no such function that would accept lambda with builder. Sorry for that.

Regarding ListSerializer(Post.serializer().descriptor) — it is equal to the listSerialDescriptor. listSerialDescriptor is just a shorthand and we're not sure it is needed at all, that's why it experimental

sandwwraith avatar May 16 '23 15:05 sandwwraith