I have several classes I want to serialize which a...
# serialization
c
I have several classes I want to serialize which are defined in a library and therefore cannot be marked as
@Serializable
. They each have many properties which are all primitives or collections of primitives, so each property should be serializable by default, with no customized serialization logic necessary. How can I make these classes serializable using the built-in serializers for their properties, rather than tediously specifying a custom serializer for each one which manually goes through the dozens of properties?
a
Are they Kotlin classes? You can try using
@Serializer(forClass = ClassFromAnotherProject::class)
https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#deriving-external-serializer-for-another-kotlin-class-experimental
c
They are Kotlin classes, but unfortunately have constructor parameters that aren't properties, so
@Serializer
doesn't seem to work. Is the only option in my case then to write a custom serializer for each one that encodes each field individually?
Although I think in the library source code they're data classes, because in the compiled code I see functions like
component()
. Would be nice if I could refer to the original data class instead, since those should work with
@Serializer
a
ah yeah, if there are non-property parameters then I don't think KxS can cope with it automatically, it would have to be manual
it depends on the classes you have to deal with, but it shouldn't be that hard. Okay, it's a bit of a pain to set up, but it should be pretty type-safe
Copy code
class ExternalFoo(
  val x: String,
  val y: Int,
  nonPropertyParam: String
) {

}

typealias ExternalFooSerialized = @Serializable(with = ExternalFooSerializer::class) ExternalFoo

object ExternalFooSerializer : KSerializer<ExternalFoo> {

  @Serializable
  class DelegateFoo(
    val x: String,
    val y: Int,
  )

  private val delegateSerializer get() = DelegateFoo.Companion.serializer()

  override val descriptor: SerialDescriptor get() = delegateSerializer.descriptor

  override fun deserialize(decoder: Decoder): ExternalFoo {
    val delegate = delegateSerializer.deserialize(decoder)
    return ExternalFoo(
      delegate.x,
      delegate.y,
      "blah",
    )
  }

  override fun serialize(encoder: Encoder, value: ExternalFoo) {
    val delegate = DelegateFoo(
      value.x,
      value.y
    )
    delegateSerializer.serialize(encoder, delegate)
  }

}
c
Thanks, that approach makes sense! I do feel like there should be a way to get it working with
@Serializer
though, considering the class in question is a data class, and in my own source code I'm able to throw
@Serializable
on data classes without any extra steps. But since the data class in the library is already compiled, I guess it gets compiled down to:
Copy code
public final data class MyClass public constructor(prop1: Double, prop2: DoubleArray) {
    val prop1: Double = prop1
    val prop2: DoubleArray = prop2
    // functions for equals, hashCode, component1, component2
}
Since it's functionally the same as any other data class, and in the source code it would be defined with constructor properties (like my own data classes which I can throw
@Serializer
on without any extra steps), it seems like a shortcoming of kotlinx serialization if it can't autogenerate the serializer for this one like it can with other data classes.
a
yeah, that’s basically what
@Serializer(forClass = ClassFromAnotherProject::class)
does - it tells the KxS plugin to generate a serializer for an external class. But if that class doesn’t meet the requirements (e.g. all parameters of the class’s primary constructor be properties), then it can’t auto-generate a serializer no matter where the class is. I don’t think there’s a sensible way to automate a solution.
c
right, I guess I just found it surprising that data classes can only meet those requirements if they're in your own source code, but data classes in libraries all lose that compatibility, even if they meet the requirements in the source code
a
oh, maybe I misunderstood, I thought you said that the external classes didn't meet the requirements
c
The code snippet I posted above is what I'm seeing when I view the external class, which doesn't meet the constructor property requirement, but that code is a data class right? So in the library's source code it's probably defined like so:
Copy code
data class MyClass(
    val prop1: Double,
    val prop2: DoubleArray
)
which would meet the requirements.
so it would be nice of kotlinx serialization could use the data class's original definition, rather than the compiled down version, or if kotlin could compile down data classes in a way where they don't lose the requirements for serialization