Bob*_*sky 5 kotlin kotlinx.serialization kotlinx
我正在尝试使用和发出包含多态项目列表的 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"
}
Run Code Online (Sandbox Code Playgroud)
正如有关多态性的文章中所述,我创建了类的层次结构。我还尝试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()
Run Code Online (Sandbox Code Playgroud)
但我得到的只是这个错误:
kotlinx.serialization.json.internal.JsonDecodingException:未找到类鉴别器“0”的多态序列化器
鉴于我无法更改 JSON 的格式,如何才能成功序列化/反序列化它?
aSe*_*emy 11
在 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")
}
}
}
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
但是,为了完整性,您可能希望包含它,因此在序列化时将其包含在内。为此,您必须使用@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
}
// ...
Run Code Online (Sandbox Code Playgroud)
将所有内容放在一起,这是一个完整的示例。
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
}
Run Code Online (Sandbox Code Playgroud)
这打印
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)
]
)
]
)
Run Code Online (Sandbox Code Playgroud)
和
{
"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
}
]
}
Run Code Online (Sandbox Code Playgroud)
与输入匹配!
归档时间: |
|
查看次数: |
3076 次 |
最近记录: |