Kotlinx.serialization: Can't serialize instances of class hierarchy with constructor parameters

Created on 7 Nov 2019  Â·  7Comments  Â·  Source: Kotlin/kotlinx.serialization

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

I have a web app that displays some complex reports. There is a class hierarchy of "queries" that I'd like to serialize to/from server. But something like this is not serializable:

abstract class Query(val type: QueryType) { ... }
abstract class QueryBase(type: QueryType) :Query(type) { ... }
class SpecificQuery1() : QueryBase(QueryType.Specific1) { ... }

The error is:

This class is not serializable automatically because it has primary constructor parameters of which are not properties

The type sort of is a property - it becomes a property once it's passed up to the root class.

I put a custom serializer on QueryBase, with method bodies of TODO("not implemented"), because I'm not sure yet how I could do it, or if it's possible.

@Serializable(with = QueryBaseSerializer::class)
abstract class QueryBase(queryType: QueryType)

I then get another error on SpecificQuery1, suggesting that even if I figured out how to write a serializer for QueryBase, I will have more problems.

Impossible to make this class serializable because its parent is not serializable and does not have exactly one constructor without parameters

Describe the solution you'd like

I'd like the auto-generated serializers to handle it. If that's not possible, I'd like documentation on how to implement custom serializers to handle it. If I need to write custom serializers, it would be nice if there were a way to use some of the plugin's code-generation abilities, so that I don't have to remember to manually serialize each property and keep that code in sync if I add properties to a class. In other words, maybe the plugin can't generate the whole serializer for me, but it could generate something to serialize the properties that it can handle, so I could call that from my serializer once I handled the custom part.

feature question

Most helpful comment

Hi,
Unfortunately, handling arbitrary arguments that are not properties is not an easy task — because they may be involved in arbitrary expressions that should be default values or superclass constructor arguments (e.g. you have class QueryBase(type: QueryType) :Query(type + "myQuery"))

We usually recommend follow this pattern when serializing class hierarchies: make vals abstract in base classes and override them in concrete classes, like this:

abstract class Query { 
    abstract val type: QueryType
}
abstract class QueryBase(override val type: QueryType) :Query() { ... }

All 7 comments

I'd like to bump this feature request, it would be really useful in my case.

I have configuration files that are shared between multiple services. All services configurations have a lot of values in common, stored in one abstract class, then each service defines a subclass adding some specific properties.

It is not a polymorphic serialization in the sense that I know which subclass I am deserializing.

Hi, do you plan to support this use case at some point? Thank you for your answer. :smiley:

Hi,
Unfortunately, handling arbitrary arguments that are not properties is not an easy task — because they may be involved in arbitrary expressions that should be default values or superclass constructor arguments (e.g. you have class QueryBase(type: QueryType) :Query(type + "myQuery"))

We usually recommend follow this pattern when serializing class hierarchies: make vals abstract in base classes and override them in concrete classes, like this:

abstract class Query { 
    abstract val type: QueryType
}
abstract class QueryBase(override val type: QueryType) :Query() { ... }

@sandwwraith Is there anything in the works for this? While the provided solution compiles, it doesn't produce the cleanest code.

Hello @CreamyCookie, what about this snippet?

import kotlinx.serialization.Serializable

@Serializable
sealed class TimerState {
    abstract val millis: Long

    @Serializable
    class STOPPED(override val millis: Long) : TimerState()

    @Serializable
    class TICKING(override val millis: Long) : TimerState()
}

@AbdElraoufSabri Huh, isn't that just an implementation of the pattern suggested by @sandwwraith ?

@CreamyCookie exactly 😂

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Egorand picture Egorand  Â·  3Comments

raderio picture raderio  Â·  3Comments

kastork picture kastork  Â·  3Comments

altavir picture altavir  Â·  4Comments

Chris-Corea picture Chris-Corea  Â·  3Comments