realm-kotlin
realm-kotlin copied to clipboard
Failed to decode RealmList from primitive array
I'd like to store an array of primitives in my Realm object, so I am declaring it like:
@Serializable
class Some: RealmObject {
@PrimaryKey
var id: String = uuid4().toString()
private var list: RealmList<Int> = realmListOf()
}
When trying to decode it from the server response I get Expected class kotlinx.serialization.json.JsonObject as the serialized body of kotlinx.serialization.Polymorphic<RealmList>, but had class kotlinx.serialization.json.JsonArray, which is gone in case I mark this list property as @Transient.
I am using Ktor with Json serializer:
val httpClient = HttpClient {
expectSuccess = true
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
encodeDefaults = true
prettyPrint = true
})
}
install(HttpTimeout) {
requestTimeoutMillis = 15000
}
}
Am I missing something? Or RealmList needs a custom serializer to be specified for the class which is using it?
Hi @grigorevp
You can add a custom serializer for the RealmList as follow
object RealmListSerializer : KSerializer<RealmList<String>> {
override fun deserialize(decoder: Decoder): RealmList<String> {
val list = realmListOf<String>()
val element: JsonElement = (decoder as JsonDecoder).decodeJsonElement()
for (i in 0 until element.jsonArray.size) {
list.add(element.jsonArray[i].jsonPrimitive.content)
}
return list
}
override fun serialize(encoder: Encoder, value: RealmList<String>) {
TODO("Not yet implemented")
}
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("RealmList<String>", PrimitiveKind.STRING)
}
You can then annotate the field like
@Serializable
class Book : RealmObject {
@SerialName("author_name")
@Serializable(with = RealmListSerializer::class)
var authors: RealmList<String> = realmListOf()
}
Yeah, thanks! Just wanted to check if I'm not missing some built-in solution
Hi @nhachicha! Need your help again, now I am trying to serialize a RealmList of RealmOject's, and am getting the following exception:
Class 'ManagedRealmList' is not registered for polymorphic serialization in the scope of 'RealmList'.
I have the following setup:
@Serializable
class Some: RealmObject {
@PrimaryKey
var id: String = uuid4().toString()
var cardHolder: CardHolder? = CardHolder()
}
@Serializable
class CardHolder: RealmObject {
@PrimaryKey
var id: String = uuid4().toString()
var cards: RealmList<Card> = realmListOf(Card())
}
@Serializable
class Card: RealmObject {
@PrimaryKey
var id: String = uuid4().toString()
var items: RealmList<Item> = realmListOf(Item())
}
@Serializable
class Item: RealmObject {
@PrimaryKey
var id: String = uuid4().toString()
}
Am I doing anything wrong? Seems I won't be able to make anything with ManagerRealmList serialization, as it is not exposed
Or will you advise to keep realm's layer separate to network one and provide some mapping to custom classes (which in this case seems strange for me)?
Hi @grigorevp I think you need to build a custom serializer for each type and combine them... example
// Decode a RealmList of Item
object RealmListItemSerializer : KSerializer<RealmList<String>> {
// similar to https://github.com/realm/realm-kotlin/issues/920#issuecomment-1175305134
...
}
// Decode Card
object CardSerializer : KSerializer<Card>> {
override val descriptor: SerialDescriptor
get() = buildClassSerialDescriptor("my.package.Card") {
element<String>("id") // kotlinx.serialization.descriptors.element
element<RealmList<Item>>("items")
}
override fun deserialize(decoder: Decoder): Card {
return decoder.decodeStructure(descriptor) {
val idField = decodeStringElement(descriptor, 0)
val itemsField: RealmList<Item> =
decodeSerializableElement(descriptor, 1, RealmListItemSerializer)
Card().apply { id = idField; items = itemsField }
}
}
override fun serialize(encoder: Encoder, value: Card) {
TODO("Not yet implemented")
}
}
// Similarly decode CardHolder using CardSerializer
object CardHolderSerializer : KSerializer<CardHolder>> {
override val descriptor: SerialDescriptor
get() = buildClassSerialDescriptor("my.package.CardHolder") {
element<String>("id")
element<RealmList<Card>>("cards")
}
override fun deserialize(decoder: Decoder): CardHolder {
return decoder.decodeStructure(descriptor) {
val idField = decodeStringElement(descriptor, 0)
val cardsField: RealmList<Card> =
decodeSerializableElement(descriptor, 1, CardSerializer)
CardHolder().apply { id = idField; card = cardsField }
}
}
override fun serialize(encoder: Encoder, value: CardHolder) {
TODO("Not yet implemented")
}
}
Then apply the custom deserializer
@Serializable
class Some: RealmObject {
@PrimaryKey
var id: String = uuid4().toString()
@Serializable(with = CardHolderSerializer::class)
var cardHolder: CardHolder? = CardHolder()
}
Closing due to lack of feedback. @grigorevp If you still need assistance with this please let us know.