question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Inheritance/polymorphic serialization

See original GitHub issue

I have the following hierarchy:

@Serializable
sealed class Search(@SerialName("type") private var searchType: SearchType,
                    @Transient open val table: String = "",
                    @Transient open val index: String? = null,
                    @Transient open val filters: List<QueryCondition> = listOf(),
                    @Transient open val order: Order = Order.DESC) {
    fun isAscOrdered(): Boolean {
        return order == Order.ASC
    }
}

@Serializable
class QuerySearch(override val table: String,
                       override val index: String?,
                       val keys: List<QueryCondition>,
                       override val filters: List<QueryCondition>,
                       override val order: Order) : Search(SearchType.QUERY, table, index, filters, order) {
...
}

@Serializable
class ScanSearch(override val table: String,
                 override val index: String?,
                 override val filters: List<QueryCondition>) :
        Search(SearchType.SCAN, table, index, filters, Order.ASC) {
...
}

And it already looks “ugly” in order to do the proper serialization, i.e. have to mark parent properties as open, and override them (in order to make them properties, as required by the serialization library) in the children.

Also in order to not have parent properties duplicated in the output JSON, have to mark them @Transient, but it then requires to set the default values (where there are no really meaningful default values).

Only then it produces the proper JSON, like the one below:

{
  "type": "QUERY",
  "table": "Table A",
  "index": "Index A",
  "keys": [
    {
      "name": "Id",
      "type": "NUMBER",
      "operator": "EQ",
      "values": [
        "10"
      ]
    }
  ],
  "filters": [
    {
      "name": "Timestamp",
      "type": "NUMBER",
      "operator": "BETWEEN",
      "values": [
        "100",
        "200"
      ]
    },
    {
      "name": "Name",
      "type": "STRING",
      "operator": "BEGINS_WITH",
      "values": [
        "Test"
      ]
    }
  ],
  "order": "ASC"
}

Although when deserialize the JSON back to the class, the parent properties are set to the default values instead of the values from the child class.

And moreover don’t know how to do the polymorphic deserialization, i.e. using the discriminator value, like “type” property.

Is then this library suitable for the above use case, or it is the ultimate end goal, but it is not ready yet to handle this?

Or this library was specifically designed in mind to work only with data classes?

Here is the unit test to produce the JSON above:

class SearchSpec : StringSpec({
    "serialize query search" {
        val search = QuerySearch(
                "Table A",
                "Index A",
                listOf(QueryCondition("Id", Type.NUMBER, Operator.EQ, listOf("10"))),
                listOf(QueryCondition("Timestamp", Type.NUMBER, Operator.BETWEEN, listOf("100", "200")),
                        QueryCondition("Name", Type.STRING, Operator.BEGINS_WITH, listOf("Test"))),
                Order.ASC)
        val data = Json.stringify(QuerySearch.serializer(), search)
        println(data)
        val parse = Json.parse(Search.serializer(), data) // Obviously this line fails, 
        // val parse = Json.parse(QuerySearch.serializer(), data) // but this works, but results in the default properties for the parent Search class
        parse shouldBe search
    }
})

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:4
  • Comments:13 (3 by maintainers)

github_iconTop GitHub Comments

18reactions
valeriyocommented, Jun 20, 2019

Inheritance doesn’t seem to work at all…

import kotlinx.serialization.Serializable

@Serializable
open class Fruit(
  val name: String? = null
)

@Serializable
class Apricot(
  name: String
): Fruit(name)

==> IllegalStateException: Class Apricot have constructor parameters which are not properties and therefore it is not serializable automatically

AND

import kotlinx.serialization.Serializable

@Serializable
open class Fruit(
  open val name: String? = null
)

@Serializable
class Apricot(
  override val name: String
): Fruit(name)

==> IllegalStateException: class Apricot has duplicate serial name of property name, either in it or its parents.

3reactions
raqfccommented, Jun 16, 2022

I’m running into a “usability” issue with inheritance that is similar to the one in the first message. I have a legacy API with large objects which can be of several types, and have a couple dozen shared fields in the JSON (and some that are type-specific).

Based on the current design, I need to create a sealed class (or interface), and then a set of concrete classes, one per type. I have to declare the shared fields in the sealed class, and then override all of them in the inheriting classes (and pass the values to the base class constructor). This means:

* **Lots** of repeated code, as I have to declare the overriding values in each subclass.

* What's more, if I want to declare a default value for a shared property, I have to do it in _each_ subclass (properties in the base class must be abstract or they won't be decoded).

Example:

@Serializable(with = ...) // uses a `JsonContentPolymorphicSerializer`
sealed class MyBaseClass {
    val property: String? = null
}

@Serializable data class A(val anotherProperty: String) : MyBaseClass()

@Serializable data class B(val aProperty: Float) : MyBaseClass()

The above fails decoding of a List<MyBaseClass> with the following error:

Encountered unknown key ‘property’. Use ‘ignoreUnknownKeys = true’ in ‘Json {}’ builder to ignore unknown keys.

To make it work, I’d have to override property in each superclass, and give it the same default value. This doesn’t seem viable for large classes with many properties, and it leads to a really poor inheritance design, and error-prone duplication of code. Is there a solution around this? Thank you!

i’m encountering the same issue, i’m using a class that inherits from an abstract class but with with its own variables (they aren’t open nor abstract), i can’t alter the super class since its from a library, but i do need the variables to be encoded aswell, any suggestions?

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to serialize properties of derived classes with System ...
In this article. Serialize properties of derived classes; Polymorphic type discriminators; Configure polymorphism with the contract model ...
Read more >
Polymorphism and Inheritance with Jackson - OctoPerf
Learn how to serialize and deserialize polymorphic object trees with Jackson Json Databind. Using practical code examples to make it easy to ...
Read more >
Polymorphic Serialization with .NET System.Text.Json
So you've decided you needed to use inheritance within your object model but are struggling to serialize all the data present on the ......
Read more >
Object Serialization with Inheritance in Java - GeeksforGeeks
Serialization is a mechanism of converting the state of an object into a byte stream. The byte array can be the class, version,...
Read more >
Inheritance in Jackson | Baeldung
Sometimes, some properties inherited from superclasses need to be ignored during serialization or deserialization. This can be achieved by one ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found