You have to build a custom RealmListSerializer in order to make it work.
Under the current version 1.0.1 of kotlinx.serialization there is no ListSerializer you might extend from.
This means you have to build your own one from scratch.
Luckily some sort of ListSerializer exists in the library, but it is marked as internal and thus not accessible to you in code.
Nevertheless I was able to write a serializer based on the ones found in the library. Everything is based off of ArrayListSerializer
and its parent classes.
CAUTION! These classes are marked as experimental. It is very likely that their implementation may change, which will break all behaviour. Errors are to be expected.
The classes copied from are ArrayListSerializer
, ListLikeSerializer
, AbstractCollectionSerializer
in package kotlinx.serialization.internal.CollectionSerializer.kt
and ArrayClassDesc
, ListLikeDescriptor
in package kotlinx.serialization.internal.CollectionDescriptors.kt
.
Here is the code: RealmListSerializer.kt
import io.realm.RealmList
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.SerialKind
import kotlinx.serialization.descriptors.StructureKind
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@Serializer(forClass = RealmList::class)
class RealmListSerializer<E>(private val dataSerializer : KSerializer<E>) : KSerializer<RealmList<E>> {
fun builder(): ArrayList<E> = arrayListOf()
private fun ArrayList<E>.toResult() : RealmList<E> {
val realmList = RealmList<E>()
for (i in this) {
realmList.add(i)
}
return realmList
}
private fun merge(decoder: Decoder): RealmList<E> {
val builder = builder()
val startIndex = builder.size
val compositeDecoder = decoder.beginStructure(descriptor)
if (compositeDecoder.decodeSequentially()) {
readAll(compositeDecoder, builder, startIndex, readSize(compositeDecoder, builder))
} else {
while (true) {
val index = compositeDecoder.decodeElementIndex(descriptor)
if (index == CompositeDecoder.DECODE_DONE) break
readElement(compositeDecoder, startIndex + index, builder)
}
}
compositeDecoder.endStructure(descriptor)
return builder.toResult()
}
override val descriptor : SerialDescriptor = RealmListDescriptor(dataSerializer.descriptor)
override fun serialize(encoder : Encoder, value : RealmList<E>) {
val size = value.size
val composite = encoder.beginCollection(descriptor, size)
val iterator = value.iterator()
for (index in 0 until size)
composite.encodeSerializableElement(descriptor, index, dataSerializer, iterator.next())
composite.endStructure(descriptor)
}
override fun deserialize(decoder : Decoder) : RealmList<E> = merge(decoder)
private fun readSize(decoder: CompositeDecoder, builder: ArrayList<E>): Int {
val size = decoder.decodeCollectionSize(descriptor)
builder.ensureCapacity(size)
return size
}
private fun readElement(decoder: CompositeDecoder, index: Int, builder: ArrayList<E>, checkIndex: Boolean = true) {
builder.add(index, decoder.decodeSerializableElement(descriptor, index, dataSerializer))
}
private fun readAll(decoder: CompositeDecoder, builder: ArrayList<E>, startIndex: Int, size: Int) {
require(size >= 0) { "Size must be known in advance when using READ_ALL" }
for (index in 0 until size)
readElement(decoder, startIndex + index, builder, checkIndex = false)
}
}
class RealmListDescriptor(private val elementDescriptor : SerialDescriptor) : SerialDescriptor {
override val kind: SerialKind get() = StructureKind.LIST
override val elementsCount: Int = 1
override fun getElementName(index: Int): String = index.toString()
override fun getElementIndex(name: String): Int =
name.toIntOrNull() ?: throw IllegalArgumentException("$name is not a valid list index")
override fun isElementOptional(index: Int): Boolean {
require(index >= 0) { "Illegal index $index, $serialName expects only non-negative indices"}
return false
}
override fun getElementAnnotations(index: Int): List<Annotation> {
require(index >= 0) { "Illegal index $index, $serialName expects only non-negative indices"}
return emptyList()
}
override fun getElementDescriptor(index: Int): SerialDescriptor {
require(index >= 0) { "Illegal index $index, $serialName expects only non-negative indices"}
return elementDescriptor
}
override val serialName : String
get() = "RealmListSerializer"
}
Afterwards you can use this class inside your RealmObject
classes like this:
@Serializable
open class Person(
@PrimaryKey var id: Long = 0,
var name: String = "",
var age: Int = 0,
@Serializable(with = RealmListSerializer::class)
var dogs: RealmList<Dog> = RealmList()
): RealmObject()
Alternatively if you use a lot of RealmList
s inside one of your RealmObejct
s you can apply this (and other serializers) to the whole file.
@file:UseSerializers(RealmListSerializer::class)
Again, please use with caution. The classes this is created from are still experimental and may change at any time without notice.