微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在使用 kotlinx.serialization 时为接口注册全局类型适配器,而不注释每个类?

如何解决如何在使用 kotlinx.serialization 时为接口注册全局类型适配器,而不注释每个类?

Gson 会让我做一个 GsonBuilder().registerTypeAdapter(MyInterface::class.java,MyConcreteClassAdapter()) 但我不能用 kotlinx.serialization

我只想公开接口 (ValueInterface),所以我可以隐藏实现和序列化细节。但是,该接口是许多类的字段,例如 Box,我不想通过使用 ValueInterface

注释每个 @Serializable(with = SomeDeserializer::class) 字段来泄露序列化细节

请注意,ValueObject 具有自己的自定义序列化。

以下代码kotlinx.serialization.SerializationException: Class 'ValueObject' is not registered for polymorphic serialization in the scope of 'ValueInterface'. Mark the base class as 'sealed' or register the serializer explicitly. 中断。

但是,当我将 Box 值类型更改为 ValueObject 而不是 ValueInterface 时,它会起作用。

我需要更改什么才能使其像 Gson 一样工作并能够拥有 ValueInterface 类型的字段?

package kxs

import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encodetoString
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import org.junit.Assert
import org.junit.Test

class KXSTest {

    @Test
    fun test(){
        val actualStr = Json.encodetoString(Box("name1",ValueInterface.create(42)))
        Assert.assertEquals("""{"name":"name1","value":42}""",actualStr)

        val actualObj: Box = Json.decodeFromString("""{"name":"name2","value":43}""")
        Assert.assertEquals(Box("name2",ValueInterface.create(43)),actualObj)

    }
}

// public stuff
interface ValueInterface {
    fun value() : Long

    companion object {
        fun create(long: Long) =
            ValueObject(long)
    }
}

@Serializable
data class Box(val name: String,val value: ValueInterface)


//internal details not meant to be exposed
@Serializable(with = ValueObjectAsLong::class)
data class ValueObject(val value: Long): ValueInterface {
    init {
        require(value > 0)
    }
    override fun value(): Long = value
}

object ValueObjectAsLong : KSerializer<ValueObject> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ValueObjectAsLong",PrimitiveKind.LONG)

    override fun deserialize(decoder: Decoder): ValueObject {
        return ValueObject(decoder.decodeLong())
    }

    override fun serialize(encoder: Encoder,value: ValueObject) {
        encoder.encodeLong(value.value)
    }
}

解决方法

要处理 ValueInterface 字段的序列化,只需将 @Serializable(with = ValueObjectAsLong::class) 注释从 ValueObject 类移动到 ValueInterface。使用无参数 @Serializable 注释接口是不可能的,但是如果您为 with 参数提供一个值,它可以很好地编译:

@Serializable(with = ValueObjectAsLong::class)
interface ValueInterface { /*...*/ }

如果您需要序列化 ​​3rd 方接口,这有点棘手。 首先,你需要为接口定义一个序列化器,而不是它的实现类:

object ValueInterfaceAsLong : KSerializer<ValueInterface> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ValueInterfaceAsLong",PrimitiveKind.LONG)
    override fun serialize(encoder: Encoder,value: ValueInterface) = encoder.encodeLong(value.value())
    override fun deserialize(decoder: Decoder) : ValueInterface = ValueObject(decoder.decodeLong())
}

然后将 @file:UseSerializers(ValueInterfaceAsLong::class) 注释添加到所有使用 ValueInterface 字段声明可序列化类的文件中。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。