【问题标题】:Kotlinx.Serializer - Create a quick JSON to sendKotlinx.Serializer - 创建一个快速的 JSON 发送
【发布时间】:2019-08-20 09:01:19
【问题描述】:

我一直在玩 Kotlinx.serialisation。我一直在尝试找到一种快速的方法来使用 Kotlinx.serialisation 来创建一个简单的 JSON(主要是发送它),同时尽量减少代码混乱。

对于一个简单的字符串如:

{"Album": "Foxtrot", "Year": 1972}

我一直在做的事情是这样的:

val str:String = Json.stringify(mapOf(
        "Album" to JsonPrimitive("Foxtrot"),
        "Year" to JsonPrimitive(1972)))

这远非美好。我的元素大多是原始的,所以我希望我有类似的东西:

val str:String = Json.stringify(mapOf(
   "Album" to "Sergeant Pepper",
   "Year" to 1967))

此外,我很高兴有一个嵌套 JSON 的解决方案。比如:

Json.stringify(JsonObject("Movies", JsonArray(
   JsonObject("Name" to "Johnny English 3", "Rate" to 8),
   JsonObject("Name" to "Grease", "Rate" to 1))))

这会产生:

{
  "Movies": [
    {
      "Name":"Johnny English 3",
      "Rate":8
    },
    {
      "Name":"Grease",
      "Rate":1
    }
  ]
}

(不一定要美化,最好不要)

有类似的吗?

注意:使用序列化器很重要,而不是直接字符串,例如

"""{"Name":$name, "Val": $year}"""

因为连接字符串是不安全的。任何非法字符都可能分解 JSON!我不想处理转义非法字符:-(

谢谢

【问题讨论】:

  • 什么是“流氓字符”,在使用所需方法时如何防止它们?
  • 我的意思是,像 " 或 } 之类的字符或 JSON 中非法的字符。我只是不想处理转义和非法字符的所有繁琐大惊小怪。无论如何,谢谢,我会做它更清晰。
  • 是否需要 Kotlinx.serialization?我发现数据类 + gson 或 jackson 工作得非常好。我不确定这种方法是否能够针对 JVM 以外的东西。我也会像上面在 spring 控制器中显示的那样使用 mapOf 和 listOf,效果很好。
  • 是的,如果您只想序列化随机映射、列表和数据类,我建议您查看 gson。需要注意的一件事是数据类的默认值,比如 Kotlinx.serialization 可以处理。我上次检查时,Gson 和 Jackson 没有考虑到这一点。
  • 没有真正的理由使用 Kotlinx.serialisation。刚刚搜索了一个使用 Kotlin 数据类和 gradle 插件自然运行的库。我必须说,Kotlinx.serialisation 很可爱。它只适用于 Kotlin 及其类型。 Android Studio/Idea 可以很好地自动完成事情……总的来说很喜欢。

标签: json kotlin kotlinx.serialization


【解决方案1】:

这组扩展方法能满足你的需求吗?

@ImplicitReflectionSerializer
fun Map<*, *>.toJson() = Json.stringify(toJsonObject())

@ImplicitReflectionSerializer
fun Map<*, *>.toJsonObject(): JsonObject = JsonObject(map {
    it.key.toString() to it.value.toJsonElement()
}.toMap())

@ImplicitReflectionSerializer
fun Any?.toJsonElement(): JsonElement = when (this) {
    null -> JsonNull
    is Number -> JsonPrimitive(this)
    is String -> JsonPrimitive(this)
    is Boolean -> JsonPrimitive(this)
    is Map<*, *> -> this.toJsonObject()
    is Iterable<*> -> JsonArray(this.map { it.toJsonElement() })
    is Array<*> -> JsonArray(this.map { it.toJsonElement() })
    else -> JsonPrimitive(this.toString()) // Or throw some "unsupported" exception?
}

这允许您传入带有各种类型的键/值的Map,并取回它的 JSON 表示。在映射中,每个值都可以是原语(字符串、数字或布尔值)、null、另一个映射(表示 JSON 中的子节点)或上述任何一种的数组或集合。

你可以这样称呼它:

val json = mapOf(
    "Album" to "Sergeant Pepper",
    "Year" to 1967,
    "TestNullValue" to null,
    "Musicians" to mapOf(
        "John" to arrayOf("Guitar", "Vocals"),
        "Paul" to arrayOf("Bass", "Guitar", "Vocals"),
        "George" to arrayOf("Guitar", "Sitar", "Vocals"),
        "Ringo" to arrayOf("Drums")
    )
).toJson()

这将返回以下 JSON,如您所愿,未经过美化:

{"Album":"Sergeant Pepper","Year":1967,"TestNullValue":null,"Musicians":{"John":["Guitar","Vocals"],"Paul":["Bass","Guitar","Vocals"],"George":["Guitar","Sitar","Vocals"],"Ringo":["Drums"]}}

您可能还想为其他一些类型添加处理,例如日期。

但是我可以检查一下您是否希望以这种方式在代码中手动构建 JSON,而不是为所有 JSON 结构创建数据类并以这种方式序列化它们?我认为这通常是处理这类事情的更标准的方式。虽然也许您的用例不允许这样做。

还值得注意的是,代码必须使用ImplicitReflectionSerializer 注释,因为它使用反射来确定每个位使用哪个序列化程序。这仍是实验性功能,未来可能会发生变化。

【讨论】:

  • 嗯,这比我放弃时所做的更漂亮。我在没有扩展的情况下或多或少地做了同样的事情。我想,这是最好的。
猜你喜欢
  • 1970-01-01
  • 2015-06-13
  • 1970-01-01
  • 1970-01-01
  • 2022-01-18
  • 2016-02-23
  • 2014-07-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多