Kotlin 可序列化任何项目

问题描述 投票:0回答:1

我遇到了数据序列化的问题。 REST API 不是我的,我无法更改它。对于某个请求,我收到一个对象:

data class State(
   val instance: String,
   val value: Any
)

各种类型的数据都可以作为对象的值参数,例如

Boolean, String, Int, Float
甚至
Object
,例如:

"state": {
   "instance": "rgb",
   "value": 13910520    
}
// OR
"state": {
   "instance": "hsv",
   "value": {
      "h": 255,
      "s": 100,
      "v": 50
   }
}

在这种情况下,值类型取决于实例(实例始终是 String 类型)。在这个答案的帮助下,我或多或少能够理解各种类型,但我不明白如果收到一个对象响应,我应该如何工作。

@Serializable(with = StateSerializer::class)
data class StateObject(
    val instance: String,
    val value: Any
)
object StateSerializer : KSerializer<StateObject> {
    private val dataTypeSerializers: Map<String, KSerializer<Any>> =
        mapOf(
            "..." to serialDescriptor<Boolean>(),
            "..." to serialDescriptor<String>(),
            "..." to serialDescriptor<Int>()
        ).mapValues { (_, v) -> v as KSerializer<Any> }

    private fun getValueSerializer(instance: String): KSerializer<Any> =
        dataTypeSerializers[instance] ?: throw SerializationException()

    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("StateObject") {
        element("instance", serialDescriptor<String>())
        element("value", buildClassSerialDescriptor("Any"))
    }

    override fun deserialize(decoder: Decoder): StateObject = decoder.decodeStructure(
        descriptor) {
        if (decodeSequentially()) {
            val instance = decodeStringElement(descriptor, 0)
            val value = decodeSerializableElement(
                descriptor,
                1,
                getValueSerializer(instance)
            )
            StateObject(instance, value)
        } else {
            require(decodeElementIndex(descriptor) == 0) {  }
            val instance = decodeStringElement(descriptor, 0)
            val value = when (val index = decodeElementIndex(descriptor)) {
                1 -> decodeSerializableElement(descriptor, 1, getValueSerializer(instance))
                CompositeDecoder.DECODE_DONE -> throw SerializationException("value field is missing")
                else -> error("Unexpected index: $index")
            }
            StateObject(instance, value)
        }
    }

    override fun serialize(encoder: Encoder, value: StateObject) {
        encoder.encodeStructure(descriptor) {
            encodeStringElement(descriptor, 0, value.instance)
            encodeSerializableElement(
                descriptor,
                1,
                getValueSerializer(value.instance),
                value.value
            )
        }
    }
}
kotlin serialization deserialization
1个回答
0
投票

最简单的方法可能是首先避免使用

Any
。您可能无法更改 REST API,但您可以更改数据类。您应该为每种特定类型创建单独的数据类,而不是仅使用一个具有
Any
值的数据类:

sealed interface State {
    val instance: String
    val value: Any

    @Serializable
    data class RGB(
        override val instance: String,
        override val value: Int,
    ) : State

    @Serializable
    data class HSV(
        override val instance: String,
        override val value: HSVValues,
    ) : State
}

HSVValues
是这样的:

@Serializable
data class HSVValues(
    val h: Int,
    val s: Int,
    val v: Int,
)

只需将剩余的案例添加到界面即可。字符串状态和布尔状态可能如下所示:

@Serializable
data class SomeString(
    override val instance: String,
    override val value: String,
) : State

@Serializable
data class SomeBoolean(
    override val instance: String,
    override val value: Boolean,
) : State

现在您可以使用像这样的简单多态序列化器:

object StateSerializer : JsonContentPolymorphicSerializer<State>(State::class) {
    override fun selectDeserializer(element: JsonElement): DeserializationStrategy<State> {
        return when (element.jsonObject["instance"]?.jsonPrimitive?.content) {
            "rgb" -> State.RGB.serializer()
            "hsv" -> State.HSV.serializer()
            "..." -> State.SomeString.serializer()
            "..." -> State.SomeBoolean.serializer()
            else -> throw SerializationException("Unknown instance ${element.jsonObject["instance"]}")
        }
    }
}

现在只需使用序列化器注释接口即可:

@Serializable(with = StateSerializer::class)
sealed interface State {
    ...
}

为每种类型的实例使用专用数据类不仅更容易序列化(反)序列化,而且还更容易使用剩余的代码,因为您不必在运行时处理某些未知的

Any
类型。相反,您可以在编译时使用正确的类型。

© www.soinside.com 2019 - 2024. All rights reserved.