I'm using Enums a fair amount in my (data class) m...
# serialization
d
I'm using Enums a fair amount in my (data class) model properties in commonMain (Kotlin MPP). I'm trying to serialize them to JSON. I get the following error when doing so:
Enums cannot be serialized polymorphically with 'type' parameter. You can use 'JsonBuilder.useArrayPolymorphism' instead
My serialization is set up as follows:
Copy code
val JSON = Json {
    ignoreUnknownKeys = true
    encodeDefaults = true
    explicitNulls = false
}
My enums follow this kind of pattern:
Copy code
@Serializable
@JsonClassDiscriminator("ItemProperty")
sealed interface ItemProperty {
    @Serializable
    @SerialName("ArmorProperty")
       enum class ArmorProperty : ItemProperty {
        @SerialName("Light") Light,
        @SerialName("Medium") Medium,
        @SerialName("Heavy") Heavy,
        @SerialName("Shield") Shield;
    }

    @Serializable
    enum class FocusProperty : ItemProperty {
        @SerialName("Arcane") Arcane,
        @SerialName("Druidic") Druidic,
        @SerialName("HolySymbol") HolySymbol;
    }
}
I cannot find anything except a really simple enum example in the serialization documentation. Does anyone know what I'm doing wrong here? I am unsure what ``JsonBuilder.useArrayPolymorphism` ` is or how to use it to resolve this issue.
e
Copy code
Json {
    useArrayPolymorphism = true
}
changes the format of polymorphism
basically when you have
Copy code
@Serializable
sealed interface Base {
    @Serializable
    data class Impl(val value: String)
}
Impl.serializer()
serializes
Impl(value = "foo")
as
{"value": "foo"}
, and JSON's default polymorphic serialization of
Base
takes that and adds a
type
discriminator, e.g.
{"type": "Impl", "value": "foo"}
. that's only possible with classes/objects, not enums
useArrayPolymorphism
uses a wrapper array instead of adding a property to the object, e.g.
["Impl", {"value": "foo"}]
, which does work for non-objects
d
Is there a way to only use
ArrayPolymorphism
on the interface/enums and use the
Polymorphism
serialization for the data class that uses enums in some of it's properties? If I just the
ArrayPolymorphism
for the whole data class (plus it's enum properties), I get an array of one instance of the data class - and that is causing Kmongo to complain... One of my data classes looks like this, for example:
Copy code
@Serializable
data class Gear(
    val name: String,
    val description: String,
    ...
    val properties: List<ItemProperty>
)
Can I so something like:
Copy code
@Serializable (with = ArrayPolymorphism)
sealed interface ItemProperty {
...
}
And leave my main
json { }
config how it was? (sorry, I'm just guessing here)
e
no there isn't
you could write a custom serializer that does something similar
d
Ok, I'll give that a try.
e
this won't be as safe as the generated polymorphic sealed serializer but should work
or this, if you want to avoid registering every type manually, relying on reflection instead
d
Thanks, this is helping a lot! I'm having to use the version of registering every type manually as
class.sealedSubclasses
doesn't resolve in common code (only in jvm code).
e
ah, and even if
KClass.sealedSubclasses
worked,
KClass.serializer()
wouldn't, since that also relies on JVM reflection
d
I can serialize with this perfectly. the result is this:
Copy code
@Serializable
data class SimpleGear(
    val name: String,
    val description: String,
    val weight: Int,
    val properties: List<ItemProperty>
)
val longsword = SimpleGear(
    "Longsword",
    "Hack off heads with this",
    3,
listOf(ItemProperty.WeaponProperty.Slashing, ItemProperty.WeaponProperty.Versatile))

val jsonString = JSON.encodeToString(longsword)

println(jsonString)

println("${longsword == JSON.decodeFromString(jsonString)}")
Copy code
{
  "name": "Longsword",
  "description": "Hack off heads with this",
  "weight": 3,
  "properties": [
    [
      "ca.kittle.model.reference.ItemProperty.WeaponProperty",
      "Slashing"
    ],
    [
      "ca.kittle.model.reference.ItemProperty.WeaponProperty",
      "Versatile"
    ]
  ]
}
But when I try to deserialize I get:
Copy code
Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Any' is not found.
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
ah, and even if
KClass.sealedSubclasses
worked,
KClass.serializer()
wouldn't, since that also relies on JVM reflection
e
you need to explicitly
Copy code
JSON.decodeFromString<SimpleGear>(jsonString)
or
Copy code
JSON.decodeFromString(SimpleGear.serializer(), jsonString)
since the type is inferred as
Any
in the string
d
Thanks SO much!
This help has been invaluable.
216 Views