我遇到了数据序列化的问题。 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
)
}
}
}
最简单的方法可能是首先避免使用
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
类型。相反,您可以在编译时使用正确的类型。