not sure how to even search this, how would I spec...
# serialization
s
not sure how to even search this, how would I specify polymorphic serialization for a class where the type is at the top level, and that adjusts nested classes (tagged unions) within the top level class?
Copy code
response:
  productType: one | two
  nestedClassOne:
    anotherType: A | B
    nestedClassTwo:
      type that only shows up if productType is One
      or
      type that only shows up if productType is Two
    nestedClassThree:
      type that only shows up if anotherType is A
      type that only shows up if anotherType is B
I'm guessing I'll need to use Content-based polymorphic deserialization
is this a case for
@InheritableSerialInfo
?
Hm. Content-based doesn't seem like it will work either. I will be needing to deserialize a nested type and I won't have the productType or anotherType in the element there to even be able to deserialize it
e
this might be a case where https://github.com/Kotlin/kotlinx.serialization/issues/1147 could help, but without it sounds like a pain to try to deal with 😞
s
ugh. that seems gross. I am still designing the model, so I guess I'll just change it to suit kotlinx more. But this seems like a really common ask to me. Is this not common? I've needed this exact thing several times now and have had to change my model each time to fit the serializer library in question.
p
@snowe The cleanest/most straightforward way would be to use a delegate for (de)serialization that has both options/full flexibility and then for the actual response have a custom serializer that delegates to that delegate and does verification when constructing the actual response from the delegate.
s
@pdvrieze hm. ok. I think I understand that. Do you have any examples of that in action? Would make my life easier lol.
p
@snowe You would do something like:
Copy code
@Serializable(Response.Companion::class)
class Response(
    val productType: PTBase
) {
  private class SerialDelegate(
    val anotherType: PTBase
    val nestedClassTwo: NestedDelegate2
    val nestedClassThree: NestedDelegate3Base
  ) {
    constructor(response: Response): this(/*...*/)
    fun toResponse(): Response = TODO()
  }

  companion object: KSerializer<Response> {
    override val serialDescriptor: SerialDescriptor = TODO()
    override fun serialize(encoder: Encoder, value: Response) {
       SerialDelegate.serializer().serialize(encoder, SerialDelegate(value))
    }
    override fun deserialize(decoder: Decoder): Response {
       return SerialDelegate.serializer().deserialize(decoder).toResponse()
    }
  }
}


internal class NestedDelegate2( /* properties for both variants */)
internal class NestedDelegate3( /* properties for both variants */)
Where the
NestedDelegate2
is an internal classes for the inner types that has properties for both. Then in
toResponse
you can verify the properties on correctness. Note that the properties have to be nullable/optional.
s
@pdvrieze hm. but that would require having a NestedDelegate for every valid combination right? So for example, if I have 4 product types and 2 anotherTypes then I need 8 classes to validate this, and they will need to duplicate all the properties.
p
Not quite, you basically put all properties in the base delegate and when creating the container you decide what actual (non-delegate) type you need to create from the delegate. This should work unless you have properties with overlapping name but different name (JsonElement can help there)