根据另一个 JSON 键的值将 JSON 解码为类

2024-02-27

我正在尝试使用返回 JSON 文档的 REST API,该文档的结构取决于名为的属性的值type.

我将主类定义如下:

@Serializable class Interaction(
    val type: Byte,
    val data: InteractionData? = null
)

的结构InteractionData取决于的值type。目前这是四种可能的结构继承自的接口。

If type equals 2, data应该是一个名为ApplicationCommandData:

@Serializable class ApplicationCommandData(
    val id: String,
    val name: String
): InteractionData

If type equals 3, data应该是一个名为MessageComponentData:

@Serializable class MessageComponentData(
    val custom_id: String
): InteractionData

我怎样才能做到这一点data属性根据值被序列化为正确的类type财产?

我尝试过设置data财产给@Transient,检查值type,并创建一个新变量@SerialName设置为类内部的数据init阻止但是@SerialData对于局部变量无效。


tl;dr:跳到底部的完整示例

问题总结

你有一个多态类,并且类型由属性决定outside班级的。

{
  "type": 2,  <- extract this
  "data": {   <- determined by 'type'
    "id": "0001",
    "name": "MEGATRON"
  }
}

Kotlinx 序列化提供了处理此问题的工具 - 但它们需要一些组装。

基于 JSON 内容的多态反序列化

由于您正在使用 JSON,因此可以使用基于内容的多态反序列化 https://github.com/Kotlin/kotlinx.serialization/blob/v1.4.1/docs/json.md#content-based-polymorphic-deserialization.

这是一个初步的实现,但有一个缺陷......

object InteractionJsonSerializer : JsonContentPolymorphicSerializer<Interaction>(
  Interaction::class
) {
  override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out Interaction> {

    // extract the type from the plain JSON object
    val type = element.jsonObject["type"]?.jsonPrimitive?.intOrNull

    println("found InteractionData type: $type")

    return when (type) {
              // can't specify the type of InteractionData
      2    -> Interaction.serializer()
      3    -> Interaction.serializer()
      else -> error("unknown type $type")
    }
  }
}

不可能选择特定的序列化器,因为Interaction没有类型参数,所以让我们添加一个。

@Serializable
data class Interaction<T : InteractionData?>( // add a type parameter
  val type: Byte,
  val data: T? = null
)

现在 Kotlinx 序列化插件将生成一个序列化器 https://github.com/Kotlin/kotlinx.serialization/blob/v1.4.1/docs/serializers.md#plugin-generated-generic-serializer接受序列化器T: InteractionData。我们可以更新InteractionJsonSerializer来利用这个。

object InteractionJsonSerializer : JsonContentPolymorphicSerializer<Interaction<*>>(
  Interaction::class
) {
  override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out Interaction<*>> {
    val type = element.jsonObject["type"]?.jsonPrimitive?.intOrNull

    println("found InteractionData type: $type")

    return when (type) {
              // now the type can be specified
      2    -> Interaction.serializer(ApplicationCommandData.serializer())
      3    -> Interaction.serializer(MessageComponentData.serializer())
      else -> error("unknown type $type")
    }
  }
}

完整示例

这是一个完整的、可运行的示例,包含所有导入。

我对你的代码做了一些调整。

  • I made InteractionData a 密封接口 https://kotlinlang.org/docs/sealed-classes.html,因为它看起来合适
  • 我将课程转换为数据类 https://kotlinlang.org/docs/data-classes.html,所以 Kotlin 生成了一个很好的toString().
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive

fun main() {
  val interactionType2 =
    Json.decodeFromString(
      InteractionJsonSerializer,
      /*language=JSON*/
      """
        {
          "type": 2,
          "data": {  
            "id": "0001",
            "name": "MEGATRON"
          }
        }
      """.trimIndent()
    )

  println(interactionType2)

  val interactionType3 =
    Json.decodeFromString(
      InteractionJsonSerializer,
      /*language=JSON*/
      """
        {
          "type": 3,
          "data": {
            "custom_id": "abc123"
          }
        }
      """.trimIndent()
    )

  println(interactionType3)
}


@Serializable
data class Interaction<T : InteractionData?>(
  val type: Byte,
  val data: T? = null
)

sealed interface InteractionData

@Serializable
data class ApplicationCommandData(
  val id: String,
  val name: String
) : InteractionData

@Serializable
data class MessageComponentData(
  val custom_id: String
) : InteractionData


object InteractionJsonSerializer : JsonContentPolymorphicSerializer<Interaction<*>>(
  Interaction::class
) {
  override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out Interaction<*>> {
    val type = element.jsonObject["type"]?.jsonPrimitive?.intOrNull

    println("found InteractionData type: $type")

    return when (type) {
      2    -> Interaction.serializer(ApplicationCommandData.serializer())
      3    -> Interaction.serializer(MessageComponentData.serializer())
      else -> error("unknown type $type")
    }
  }
}

Output

found InteractionData type: 2
Interaction(type=2, data=ApplicationCommandData(id=0001, name=MEGATRON))
found InteractionData type: 3
Interaction(type=3, data=MessageComponentData(custom_id=abc123))

Versions

  • 科特林 1.7.21
  • Kotlinx 序列化 1.4.1
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

根据另一个 JSON 键的值将 JSON 解码为类 的相关文章

随机推荐

  • 如果未在命令行上指定,则仅在 psql-script 中设置变量

    我想给option在命令行上为我的 psql 脚本指定一些变量 psql v myVar myValue 但是我发现无法在 sql 脚本本身中为这些变量提供默认值 语法 set MyVar defaultValue 覆盖值myValue在
  • 在python中将一维列表转换为具有给定行长度的二维列表[重复]

    这个问题在这里已经有答案了 有没有一种简单的方法可以将一维列表转换为具有给定行长度的二维列表 假设我有一个这样的列表 myList 1 2 3 4 5 6 7 8 9 我想将上面的列表转换为 3 x 3 的表格 如下所示 myList 1
  • 测量查询性能:“执行计划查询成本”与“所用时间”

    我正在尝试确定两个不同查询的相对性能 并且有两种可用的方法来衡量它 1 运行两个查询并对每个查询计时2 运行两者并从实际执行计划中获取 查询成本 这是我运行的用于计时查询的代码 DBCC FREEPROCCACHE GO DBCC DROP
  • msi 安装程序运行两次

    我有一个通过 msi 安装的程序 msi 是使用 VS2008 部署项目构建的 并具有在安装完成后运行该程序的自定义操作 一旦 msi 运行 我可以简单地更新版本号 生成新的产品代码 并且 msi 可以在同一台 PC 上再次运行 但是 我想
  • Fancybox 宽度不适用

    使用以下 JS 宽度不会被调整 我使用的时候没有调整 750 or 750px a city prompt fancybox width 750 我已经发布在fancybox http fancybox net api论坛讨论过这个问题 但
  • Magento 扁平化产品

    尝试启用并重新索引产品平面数据时 从 magento 收到错误 平面目录模块的可过滤和 或可排序限制为 64 个 属性 目前有521个 请减少数量 可过滤 可排序的属性以便使用此模块 我不明白这意味着什么以及 magento 从哪里获取这个
  • 为什么选择下拉菜单不允许我单击某个项目 IE,但在 Firefox、Chrome 等中却可以正常工作?

    我正在使用jquery mega下拉菜单插件 http www designchemical com lab jquery mega drop down menu plugin examples 在其中一个菜单中 我想添加一个下拉框 它在
  • 如何在flutter中分割dart类?

    我做了以下测试 但它不起作用 main dart class Test static const a 10 final b 20 final c a 1 part dart part of main dart class Test fina
  • 如何在 Fortran 中将子例程名称作为参数传递?

    将子例程名称作为参数传递的语法是什么 示意图 call action mySubX argA argB subroutine action whichSub argA argB call subroutine whichSub argA a
  • 嵌入式 Tomcat 不提供静态内容

    我正在使用以下内容 基于this https stackoverflow com questions 640022 howto embed tomcat 6 创建嵌入式 Tomcat 服务器 File catalinaHome new Fi
  • 在 Delphi 7 中,我可以设置“调试”和“发布”模式吗?

    在大多数现代 IDE 中 您可以拥有 调试 和 发布 构建配置 并且可以在它们之间快速切换 在Delphi 7中 这似乎不可能 我必须进入项目设置并手动切换优化和所有调试信息 如果有一个插件或类似的插件可以帮我处理这个问题 那就太好了 有人
  • 自定义错误页面,当发生 Http 错误而不更改 url 时

    当 Http 错误发生时 如何在不更改 url 的情况下显示自定义错误页面 当发生 Http 错误时 如何显示客户自定义错误页面而不路由到另一个 URL 下面的方法不会使用重定向 它将返回您的自定义错误 正确的 httpstatus 代码作
  • 垂直对齐:中间不起作用

    CSS 属性vertical align middle在此示例中不起作用 HTML div span class twoline Two line text span span class float Float right span di
  • 创建 NSDate Monotouch

    我试图获取一个日期字符串并将其转换为特定的 NSDate 例如 1981 年 7 月 1 日 但我没有看到设置日期的方法 有谁知道如何做到这一点 也许将 DateTime 对象转换为 NSDate 最简单的方法是从 DateTime 设置它
  • sim800L GPRS 发布请求

    我一直在研究 LoNet 迷你 GSM 模块 SIM800L 并将其与 Arduino 连接 我已插入 SIM 移动卡并且可以连接互联网 通过串行监视器 我可以毫无问题地与它通信 但是当向网络服务器页面发出 GET 或 POST 请求时 它
  • 在AS3中创建链表

    如何在actionScript 3 0 中创建链接列表 我有一个项目 我应该从用户那里获取一些整数 并通过树算法对它们进行排序 例如堆排序并在闪存中显示树 我认为我应该使用链表通过树算法对数据进行排序 所以有人知道如何创建一个可以插入节点
  • 无法加载文件或程序集“Microsoft.SqlServer.Types,Version=10.0.0.0”或其依赖项之一

    最近我开始使用 SSMS 2017 v17 5 在我的 MVC 应用程序中 我收到以下错误 Could not load file or assembly Microsoft SqlServer Types Version 10 0 0 0
  • 我需要一个工具来查找单个文本文件或一组文本文件中重复或相似的文本块

    我想自动将重复或类似的 C 代码移动到函数中 这必须在 Linux 下工作 您的问题的一个子集 检测重复代码 Try PMD https pmd github io 重复的代码可能很难找到 尤其是在大型项目中 但 PMD 的复制 粘贴检测器
  • 比较器的等价恒等运算

    是否存在可能的身份表示Comparator那可能存在吗 在寻找简化代码的过程中删除Java中重载的方法 https stackoverflow com questions 58782150 removing overloaded metho
  • 根据另一个 JSON 键的值将 JSON 解码为类

    我正在尝试使用返回 JSON 文档的 REST API 该文档的结构取决于名为的属性的值type 我将主类定义如下 Serializable class Interaction val type Byte val data Interact