我正在尝试使用并发出JSON,该JSON包含一个多态项列表。问题是:项目包含带有整数值(而不是字符串)的type键。API端点生成并期望JSON类似于以下内容:
{
"startTime": "2022-07-27T13:32:57.379Z",
"items": [
{
"type": 0,
"results": "string",
"restBetweenRounds": "string"
},
{
"type": 1,
"results": "string",
"setCount": 0
},
{
"type": 2,
"items": [
{
"type": 0,
"results": "string",
"restBetweenRounds": "string"
},
{
"type": 1,
"results": "string",
"setCount": 0
}
],
"results": "string"
}
],
"status": 0,
"clientId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}正如文章中关于多态性的描述,我创建了一个类的层次结构。我还尝试在反序列化之前转换type值。
object MyTransformingDeserializer : JsonTransformingSerializer<BaseItem>(PolymorphicSerializer(BaseItem::class)) {
override fun transformDeserialize(element: JsonElement): JsonElement {
val type = element.jsonObject["type"]!!
val newType = JsonPrimitive(value = type.toString())
return JsonObject(element.jsonObject.toMutableMap().also { it["type"] = newType })
}
}
@Serializable(with = MyTransformingDeserializer::class)
sealed class BaseItem {
abstract val type: String
}
@Serializable
@SerialName("0")
class ItemType0(
override val type: String,
// ...
) : BaseItem()
@Serializable
@SerialName("1")
class ItemType1(
override val type: String,
// ...
) : BaseItem()
@Serializable
@SerialName("2")
class ItemType2(
override val type: String,
// ...
) : BaseItem()但我得到的只是这个错误:
未为类鉴别器“0”找到kotlinx.serialization.json.internal.JsonDecodingException:多态序列化器
既然我无法更改JSON的格式,那么如何成功地序列化/弃用它呢?
发布于 2022-07-27 22:37:56
在Kotlinx序列化中处理多态性很困难,特别是当您无法控制源的格式时。但是KxS确实提供了很多低级工具来手动处理几乎所有的东西。
你选择了JsonTransformingSerializer!在KxS选择序列化程序之前,它似乎不会转换JSON。因为鉴别器只能是字符串,因此反序列化失败。
JsonContentPolymorphicSerializer
而不是JsonTransformingSerializer,您可以使用JsonContentPolymorphicSerializer。
Kotlinx将首先将JSON反序列化为JsonObject。然后,它将将该对象提供给BaseItem的序列化程序,您可以解析并选择正确的子类。
import kotlinx.serialization.*
import kotlinx.serialization.json.*
object BaseItemSerializer : JsonContentPolymorphicSerializer<BaseItem>(BaseItem::class) {
override fun selectDeserializer(
element: JsonElement
): DeserializationStrategy<out BaseItem> {
return when (val type = element.jsonObject["type"]?.jsonPrimitive?.intOrNull) {
0 -> ItemType0.serializer()
1 -> ItemType1.serializer()
2 -> ItemType2.serializer()
else -> error("unknown Item type $type")
}
}
}包括type
因为这是手动执行多态识别,所以不需要在类中包括type。
import kotlinx.serialization.Serializable
@Serializable(with = BaseItemSerializer::class)
sealed class BaseItem
@Serializable
data class ItemType0(
// ...
) : BaseItem()
@Serializable
class ItemType1(
// ...
) : BaseItem()
@Serializable
class ItemType2(
// ...
) : BaseItem()但是,为了完整性起见,您可能希望将其包括在内,因此在序列化时会包括它。为此,您必须使用@EncodeDefault
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.Serializable
@Serializable(with = BaseItemSerializer::class)
sealed class BaseItem {
abstract val type: Int
}
@Serializable
class ItemType0(
// ...
) : BaseItem() {
@EncodeDefault
override val type: Int = 0
}
// ...完整示例
把它们结合在一起,这是一个完整的例子。
import kotlinx.serialization.*
import kotlinx.serialization.json.*
val mapper = Json {
prettyPrint = true
prettyPrintIndent = " "
}
fun main() {
val json = """
{
"startTime": "2022-07-27T13:32:57.379Z",
"status": 0,
"clientId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"items": [
{
"type": 0,
"results": "string",
"restBetweenRounds": "string"
},
{
"type": 1,
"results": "string",
"setCount": 0
},
{
"type": 2,
"items": [
{
"type": 0,
"results": "string",
"restBetweenRounds": "string"
},
{
"type": 1,
"results": "string",
"setCount": 0
}
],
"results": "string"
}
]
}
""".trimIndent()
val itemHolder: ItemHolder = mapper.decodeFromString(json)
println(itemHolder)
println(mapper.encodeToString(itemHolder))
}
@Serializable
data class ItemHolder(
val startTime: String,
val clientId: String,
val status: Int,
val items: List<BaseItem>,
)
@Serializable(with = BaseItem.Serializer::class)
sealed class BaseItem {
abstract val type: Int
object Serializer : JsonContentPolymorphicSerializer<BaseItem>(BaseItem::class) {
override fun selectDeserializer(
element: JsonElement
): DeserializationStrategy<out BaseItem> {
return when (val type = element.jsonObject["type"]?.jsonPrimitive?.intOrNull) {
0 -> ItemType0.serializer()
1 -> ItemType1.serializer()
2 -> ItemType2.serializer()
else -> error("unknown Item type $type")
}
}
}
}
@Serializable
data class ItemType0(
val results: String,
val restBetweenRounds: String,
) : BaseItem() {
@EncodeDefault
override val type: Int = 0
}
@Serializable
data class ItemType1(
val results: String,
val setCount: Int,
) : BaseItem() {
@EncodeDefault
override val type: Int = 1
}
@Serializable
data class ItemType2(
val results: String,
val items: List<BaseItem>,
) : BaseItem() {
@EncodeDefault
override val type: Int = 2
}这个指纹
ItemHolder(
startTime=2022-07-27T13:32:57.379Z,
clientId=3fa85f64-5717-4562-b3fc-2c963f66afa6,
status=0,
items=[
ItemType0(results=string, restBetweenRounds=string),
ItemType1(results=string, setCount=0),
ItemType2(
results=string,
items=[
ItemType0(results=string, restBetweenRounds=string),
ItemType1(results=string, setCount=0)
]
)
]
)和
{
"startTime": "2022-07-27T13:32:57.379Z",
"clientId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": 0,
"items": [
{
"results": "string",
"restBetweenRounds": "string",
"type": 0
},
{
"results": "string",
"setCount": 0,
"type": 1
},
{
"results": "string",
"items": [
{
"results": "string",
"restBetweenRounds": "string",
"type": 0
},
{
"results": "string",
"setCount": 0,
"type": 1
}
],
"type": 2
}
]
}这与输入相匹配!
版本
https://stackoverflow.com/questions/73139766
复制相似问题