Hey everyone, I was hoping to get some advice with...
# serialization
o
Hey everyone, I was hoping to get some advice with an issue I'm having with
kotlinx.serialization
when trying to achieve multiple levels of polymorphism with individual class discriminators for each level. I'm building a library that allows users to deserialize JSON returned from a headless CMS. The CMS supplies a class discriminator for every type returned in its API, but the types themselves are user-defined and may be polymorphic themselves, which is what's causing me trouble. Here's an abstract representation of some JSON that the CMS might return and that library should be able to deserialize.
Copy code
[
  {
    "cmsDiscriminator": "someUserType",
    "someUserValue": "value"
  },
  {
    "cmsDiscriminator": "otherUserType",
    "otherUserValue": "value"
  }
]
The library only supplies the base class, setting up the
cmsDiscriminator
.
Copy code
@Serializable
@JsonClassDiscriminator("cmsDiscriminator")
abstract class CmsBase
Users then extend this base class with their own types which works fine for direct subclasses of
CmsBase
.
Copy code
@Serializable
@SerialName("someUserType")
class SomeUserType(val someUserValue: String) : CmsBase()

@Serializable
@SerialName("otherUserType")
class OtherUserType(val otherUserValue: String) : CmsBase()
The problem arises when the user wants to introduce a new polymorphic hierarchy, where the base class is a subclass of
CmsBase
and has its own class discriminator, for JSON like this
Copy code
[
  {
    "cmsDiscriminator": "polymorphicUserType",
    "userDiscriminator": "somePolymorphicSubType",
    "someSubValue": "value"
  },
  {
    "cmsDiscriminator": "polymorphicUserType",
    "userDiscriminator": "otherPolymorphicSubType",
    "otherSubValue": "value"
  }
]
The users would have to define a class hierarchy like this:
Copy code
@Serializable
@SerialName("polymorphicUserType")
@JsonClassDiscriminator("userDiscriminator") // doesn't work
abstract class UserPolymorphicBase : CmsBase()

@Serializable
@SerialName("somePolymorphicSubType")
class SomePolymorphicSubType(val someSubValue: String) : UserPolymorphicBase()

@Serializable
@SerialName("otherPolymorphicSubType")
class OtherPolymorphicSubType(val otherSubValue: String) : UserPolymorphicBase()
However, here's where I'm encountering my first problem: The
@JsonClassDiscriminator
annotation can't be used on
UserPolymorphicBase
, because the
Copy code
Argument values for inheritable serial info annotation 'JsonClassDiscriminator'
must be the same as the values in parent type 'CmsBase'
, not allowing us to change the discriminator value for the subclasses of some class in that hierarchy. One way to get around that would be to have the user define and supply a
JsonContentPolymorphicSerializer
for their
UserPolymorphicBase
class, but I would rather not force that on them. Additionally, it does not save me from the second problem, which is that the library has to define a custom
SerializerModule
since the
CmsBase
class is not
sealed
. In the library, I would have to do something like this
Copy code
serializersModule = SerializersModule {
    polymorphic(CmsBase::class) {
        subclass(SomeUserType::class)
        subclass(OtherUserType::class)
        subclass(UserPolymorphicBase::class) // doesn't work
    }
    polymorphic(UserPolymorphicBase::class) {
        subclass(SomePolymorphicSubType::class)
        subclass(OtherPolymorphicSubType::class)
    }
}
But that fails because
UserPolymorphicBase
is not a concrete class and thus can't be registered as a polymorphic subclass of
CmsBase
. This issue gets worse when considering the fact that the polymorphic hierarchy can be nested arbitrarily deep as far as the user is concerned and should accordingly be supported by the library. I believe this should be possible to do with a lot of workarounds, but I was hoping not to have to implement a bunch of custom deserialization logic for this to work. Is there a way to achieve this with what we have available in
kotlinx.serialization
? Thanks in advance for any help and suggestions!