android - 用于 ReamList 和 List 的 Kotlin 序列化序列化器

标签 android kotlin realm realm-list kotlinx.serialization

我只是尝试使用 Kotlin 序列化库来替换 Gson,但是在我的模型中将其用于 RealmList 时遇到问题。任何帮助将不胜感激。我收到错误 kotlinx.serialization.SerializationException: Can't locate argument-less serializer for class io.realm.RealmList

我有这样的数据类

data class Person(
var name: String = "", 
var social : RealmList<Social> = null, 
var id: String = "") 

我的社交数据类是

data class Social(
var name: String = "", 
var category : String = "", 
var id: String = "")

这是我的改造构建器

fun provideRetrofit(okHttpClient: OkHttpClient, urlProvider :
    URLProvider): Retrofit {
    val contentType = MediaType.get("application/json")
    return Retrofit.Builder()
        .baseUrl(urlProvider.getApiBaseUrl()!!)
        .addConverterFactory(Json.asConverterFactory(contentType))
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .client(okHttpClient)
        .build()
}

正在使用特定于 Kotlin 序列化的 Retrofit 适配器 和我的改造电话

override fun loadPersons(): Single<Person> {
    return communicationContext.personApi
        .getPersonsObservable()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .doOnError {
            Log.e(TAG, "Load error $it")
        }
        .doOnSuccess {
            Log.e(TAG, "Success size ${it.size}")
        }
}

最佳答案

您必须构建一个自定义 RealmListSerializer 才能使其工作。

在 kotlinx.serialization 的当前版本 1.0.1 下,没有可以扩展的 ListSerializer。 这意味着您必须从头开始构建自己的一个。

幸运的是,库中存在某种 ListSerializer,但它被标记为内部,因此无法在代码中访问。 尽管如此,我还是能够根据库中找到的序列化器编写一个序列化器。一切都基于ArrayListSerializer及其父类。

注意!这些类被标记为实验类。它们的实现很可能会发生变化,这将破坏所有行为。错误是可以预料的。

从中复制的类是 ArrayListSerializer , ListLikeSerializer , AbstractCollectionSerializer包装中kotlinx.serialization.internal.CollectionSerializer.ktArrayClassDesc , ListLikeDescriptor包装中kotlinx.serialization.internal.CollectionDescriptors.kt .

代码如下: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"

}

之后您可以在 RealmObject 中使用此类像这样的类:

@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()

或者,如果您使用大量 RealmList位于您的 RealmObejct 之一内您可以将此(和其他序列化程序)应用于整个文件。

@file:UseSerializers(RealmListSerializer::class)

再次强调,请谨慎使用。创建的类仍处于实验阶段,可能随时更改,恕不另行通知。

关于android - 用于 ReamList 和 List 的 Kotlin 序列化序列化器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58593010/

相关文章:

Android:进度对话框在关闭时引发

java - 使用不同的行布局填充单个 ListView

android - 如何调用内部 ListView 的notifydatasetchanged?

generics - Kotlin 实体化类型参数不智能转换

java - 显示另一个 Activity 的按钮

android - 未调用 Workmanager Worker 类 doWork() 方法

android - 如何在 Kotlin 中将数组转换为集合(HashSet)?

java - 如何理解 Realm 在 android 上的行为?

android - 使用 Android 进行 Realm 加密

android - Realm.io - 如何在查询中使用通配符?