【问题标题】:How to edit nested JSON in kotlin如何在 kotlin 中编辑嵌套的 JSON
【发布时间】:2019-11-12 12:58:12
【问题描述】:

我有一个带有嵌套字段的 JSON 模型,该模型从空开始,我需要一点一点地添加/删除内容。

这是带有默认值的 JSON 结构

{
  "id": "",
  "name": "",
  "age": 0,
  "address": {
    "streetName": "",
    "streetNumber": 0
  }
}

到目前为止,我尝试将 JSON 表示为 String 和 Kotlin data class(使用 Moshi 或 Gson 库),但两者都不允许我轻松更改嵌套属性。

我想要的是执行以下操作的函数

fun <T>addToJson(key: String, value: T) {
  // Finds the key in the JSON, if nested, maybe expressing the whole path to it (i.e. address.streetName)
  // Inserts the value in it
  // Bonus points if there was a way to 'determine' the type of the field so that I don't risk to set for the key `streetNumber` a value that is `String`
}

最终目标是能够从应用程序中的任何位置调用类似addToJson("path.to.key", 25) 的键,键可以表示为String 或以任何其他简单的方式

【问题讨论】:

  • 你试过Jackson库中的ObjectNode吗?

标签: json kotlin data-class


【解决方案1】:

这个伪你问的问题。

  1. 如果同一个嵌套键的使用次数超过了你打算怎么办? 一次?
  2. T 值必须是已知的 JSON 对象类型基元数组或 JSON 对象您打算如何在运行时强制执行此操作?

这是一个关于如何访问对象内部每个属性的示例

val jsonString = "{\n" +
        "  \"id\": \"\",\n" +
        "  \"name\": \"\",\n" +
        "  \"age\": 0,\n" +
        "  \"address\": {\n" +
        "    \"streetName\": \"\",\n" +
        "    \"streetNumber\": 0\n" +
        "  }\n" +
        "}"
val gson = Gson()
val jsonElement = gson.fromJson(jsonString, JsonElement::class.java).asJsonObject
jsonElement.addProperty("id",26)
jsonElement.addProperty("name","sof")
jsonElement.addProperty("age",33)
val nested = jsonElement.get("address").asJsonObject
nested.addProperty("streetName","blala")
nested.addProperty("streetNumber",12)

print(jsonElement)

【讨论】:

  • 所以,对于被多次使用的同一个密钥,我希望能够找到一个可以在应用程序的任何地方或多或少地使用的解决方案,例如addToJson(address.streetName, "blala")
【解决方案2】:

在 Jackson (jackson-databind) 库中尝试 ObjectNode

您可以从头开始构建它

val factory = JsonNodeFactory(true)
val o = ObjectNode(factory)
o.set("id", IntNode(15))
o.set("name", TextNode("yourname"))
o.set("lastName", TextNode("lastname"))
val address = ObjectNode(factory)
o.set("address", address)
address.set("streetName", TextNode("Lenin"))
address.set("streetNumber", IntNode(5))
address.set("buildingNumber", IntNode(15))
println(o)

或加载现有的 json 并对其进行修改

val json = "{\n" +
        "  \"id\": \"\",\n" +
        "  \"name\": \"\",\n" +
        "  \"age\": 0,\n" +
        "  \"address\": {\n" +
        "    \"streetName\": \"\",\n" +
        "    \"streetNumber\": 0\n" +
        "  }\n" +
        "}"
val o : ObjectNode = (ObjectMapper()).readTree(json) as ObjectNode
o.set("id", IntNode(15))
o.set("name", TextNode("yourname"))
val address = o.get("address") as ObjectNode
address.set("streetName", TextNode("Lenin"))
address.set("streetNumber", IntNode(5))
address.set("buildingNumber", IntNode(15))
println(o)

我不知道在 Json 中支持多级路径的库。 但是实现这样的东西相当容易:

fun setInJson(root: ObjectNode, objectMapper: ObjectMapper, path: String, value: JsonNode) {
    val splitPath = path.split(":")
    val lastName = splitPath.last()
    val notLastNames = splitPath.subList(0, splitPath.size - 1)
    var node = root
    notLastNames.forEach { nodeName ->
        node = (node.get(nodeName) as ObjectNode?) ?: {
            val newNode = objectMapper.createObjectNode()
            node.set(nodeName, newNode)
            newNode
        }()
    }
    node.set(lastName, value)
}

可以用作:

val json = "{\n" +
        "  \"id\": \"\",\n" +
        "  \"name\": \"\",\n" +
        "  \"age\": 0,\n" +
        "  \"address\": {\n" +
        "    \"streetName\": \"\",\n" +
        "    \"streetNumber\": 0\n" +
        "  }\n" +
        "}"

val om = ObjectMapper()
val o : ObjectNode = (ObjectMapper()).readTree(json) as ObjectNode
setInJson(o, om, "id", IntNode(15))
setInJson(o, om, "name", TextNode("yourname"))
setInJson(o, om, "address:streetName", TextNode("Lenin"))
setInJson(o, om, "address:streetNumber", IntNode(5))
println(o)

【讨论】:

  • 谢谢 我不知道 Jackson 库中的 ObjectNode。虽然我不确定它是否会达到预期的目标,即可以从代码中的任何地方执行addToJson(address.streetName, "Lenin") 之类的操作
  • 现在我明白了,你想使用多级路径。我不知道有哪个库支持这一点,但我为 Jackson 写了一个小技巧。
  • 啊,看起来确实不错:) 我想虽然有办法“验证”path,就好像它在类的 n 个实例的字段中一样。我会试着看看我能不能做到这一点,万分感谢
  • 我不认为进行验证是不可能的。您可以使用路径(字符串)映射到类型(类)和类型检查来实现它 - 例如检查当 setInJson 被称为 'address:streetNumber' 时,该值是一个 IntNode。另一种(更规范的)方法是 json 模式(使用像 github.com/java-json-tools/json-schema-validator 这样的工具),但在每次修改后使用它并不是最佳选择,只有在整个 json 形成之后。
猜你喜欢
  • 2019-12-03
  • 2017-07-02
  • 2021-10-04
  • 1970-01-01
  • 2015-10-07
  • 1970-01-01
  • 2012-03-29
  • 2019-10-26
  • 2013-06-21
相关资源
最近更新 更多