Skip to content

Request: a tuple descriptor, so JSON messages can be smaller (currently requires InternalSerializationApi) #1906

Open
@aSemy

Description

@aSemy

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

I need to use JSON to encode and decode 2d arrays of x/y coordinates

  @Serializable
  data class Coordinates(
    val x: Int,
    val y: Int,
  )

By default, the JSON encoding is not size-optimal

{ "x": 1, "y": 2 }

Encoding a coordinate as a tuple (a fixed-length array, with fixed types per position, see the TypeScript docs) would make the messages more compact, and save a lot of space and improve processing speeds.

[1, 2]

There are other data structures I'd like to encode as well. For example, some information for each coordinate.

  @Serializable
  data class CoordinatesDetails(
    val x: Int,
    val y: Int,
    private val colour: String,
    private val active: Boolean,
  ) {
    ...
  }

Again, this would save a lot of space if I could en/decode it as a tuple

[1, 2, "red", true]
{ "x": 1, "y": 2, "colour": "red", "active": true }

Describe the solution you'd like

I've created a 'tuple descriptor builder' https://github.com/adamko-dev/kotlinx-serialization-typescript-generator/blob/main/docs/tuples.md

// tuple descriptor builder example
  @Serializable(with = Coordinates.Serializer::class)
  data class Coordinates(
    val x: Int,
    val y: Int,
  ) {
    object Serializer : TupleSerializer<Coordinates>(
      "Coordinates",
      {
        element(Coordinates::x)
        element(Coordinates::y)
      }
    ) {
      override fun tupleConstructor(elements: Iterator<*>): Coordinates {
        val x = requireNotNull(elements.next() as? Int)
        val y = requireNotNull(elements.next() as? Int)
        return Coordinates(x, y)
      }
    }
  }

This is quite finickity though, and also it's bugged. Under the hood it requires buildSerialDescriptor, because buildClassSerialDescriptor doesn't allow setting the structure kind to be LIST.


My custom tuple-serializer would be much easier if I could

  1. retain the plugin-generated serializer Keep access to plugin generated serializer when mark class with @Serializable(with = MySerializer) #1169
  2. override the serial kind of the plugin-generated descriptor and change it from CLASS to LIST.

Alternatively, it would be even easier if I could tell the compiler plugin to generate a descriptor as usual, except just change the 'kind' of the descriptor.

  @Serializable(kind = StructureKind.LIST)
  data class Coordinates(
    ...

Finally, another option is to add an arg to buildClassSerialDescriptor

public fun buildClassSerialDescriptor(
    serialName: String,
    serialKind: StructureKind = StructureKind.CLASS, // new arg, with a default 
    vararg typeParameters: SerialDescriptor,
    builderAction: ClassSerialDescriptorBuilder.() -> Unit = {}
):
...

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions