首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当类型为整数而不是字符串时,多态kotlinx序列化

当类型为整数而不是字符串时,多态kotlinx序列化
EN

Stack Overflow用户
提问于 2022-07-27 14:26:42
回答 1查看 401关注 0票数 2

我正在尝试使用并发出JSON,该JSON包含一个多态项列表。问题是:项目包含带有整数值(而不是字符串)的type键。API端点生成并期望JSON类似于以下内容:

代码语言:javascript
复制
{
  "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值。

代码语言:javascript
复制
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的格式,那么如何成功地序列化/弃用它呢?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-27 22:37:56

在Kotlinx序列化中处理多态性很困难,特别是当您无法控制源的格式时。但是KxS确实提供了很多低级工具来手动处理几乎所有的东西。

你选择了JsonTransformingSerializer!在KxS选择序列化程序之前,它似乎不会转换JSON。因为鉴别器只能是字符串,因此反序列化失败。

JsonContentPolymorphicSerializer

而不是JsonTransformingSerializer,您可以使用JsonContentPolymorphicSerializer

Kotlinx将首先将JSON反序列化为JsonObject。然后,它将将该对象提供给BaseItem的序列化程序,您可以解析并选择正确的子类。

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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
}

// ...

完整示例

把它们结合在一起,这是一个完整的例子。

代码语言:javascript
复制
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
}

这个指纹

代码语言:javascript
复制
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)
      ]
    )
  ]
)

代码语言:javascript
复制
{
  "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
    }
  ]
}

这与输入相匹配!

版本

  • 科特林1.7.10
  • Kotlinx系列化1.3.4
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73139766

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档