如何在kotlin中编辑嵌套JSON

如何在kotlin中编辑嵌套JSON,json,kotlin,data-class,Json,Kotlin,Data Class,我有一个带有嵌套字段的JSON模型,它的开头是空的,我需要一点一点地添加/删除内容 以下是JSON结构及其默认值 { "id": "", "name": "", "age": 0, "address": { "streetName": "", "streetNumber": 0 } } 到目前为止,我尝试将JSON表示为String,并通过Kotlin数据类(使用Moshi或Gson库),但这两种方法都不允许我轻松更改嵌套属性 我想要的是一个函数,它执行以下

我有一个带有嵌套字段的JSON模型,它的开头是空的,我需要一点一点地添加/删除内容

以下是JSON结构及其默认值

{
  "id": "",
  "name": "",
  "age": 0,
  "address": {
    "streetName": "",
    "streetNumber": 0
  }
}
到目前为止,我尝试将JSON表示为
String
,并通过Kotlin
数据类
(使用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`
}
fun addToJson(key:String,value:T){
//在JSON中查找键(如果嵌套),可能表示指向该键的整个路径(即address.streetName)
//在其中插入值
//如果有一种方法可以“确定”字段的类型,这样我就不会冒险为“streetNumber”键设置一个“String”值,则可以获得额外的积分`
}

最终的目标是能够从应用程序中的任何地方调用类似于
addToJson(“path.to.key”,25)
的东西,其中的键可以表示为
String
,也可以用任何其他简单的方式来表达

  • 如果同一个嵌套键被多次使用,您打算怎么做 一次
  • 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)
    

    这是你提出的一些问题

  • 如果同一个嵌套键被多次使用,您打算怎么做 一次
  • 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)
    

    尝试Jackson(Jackson数据绑定)库中的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(Jackson数据绑定)库中的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吗?您在Jackson库中尝试过ObjectNode吗?谢谢,我不知道Jackson库中的ObjectNode。虽然我不确定它是否能达到预期的目标,也就是从代码中的任何地方可以执行类似于
    addToJson(address.streetName,“Lenin”)
    现在我明白了,您想要使用多级路径。我不知道有哪一个库支持这一点,但我为Jackson写了一篇小文章。啊,看起来确实不错:)我想不可能有一种方法“验证”
    路径,就好像它在类的n个实例的字段中一样。我会试着看看我能不能用这个,但我不认为验证是不可能的。您可以通过路径(字符串)到类型(类)的映射和类型检查来实现它-例如,检查setInJson调用“address:streetNumber”时,值是否为IntNode。另一种(更规范的)方法是json模式(使用这样的工具),但在每次修改之后使用它并不是最好的,只有在整个json形成之后。谢谢,我不知道Jackson库中的ObjectNode。虽然我不确定它是否能达到预期的目标,也就是从代码中的任何地方可以执行类似于
    addToJson(address.streetName,“Lenin”)
    现在我明白了,您想要使用多级路径。我不知道有哪一个库支持这一点,但我为Jackson写了一篇小文章。啊,看起来确实不错:)我想不可能有一种方法“验证”
    路径,就好像它在类的n个实例的字段中一样。我会试着看看我能不能用这个,但我不认为验证是不可能的。您可以通过路径(字符串)到类型(类)的映射和类型检查来实现它-例如,检查setInJson调用“address:streetNumber”时,值是否为IntNode。另一种(更规范的)方法是json模式(使用这样的工具),但在每次修改之后使用它并不是最好的,只有在整个json形成之后,对于多次使用的同一个密钥,我希望能够找到一个可以在应用程序中随处使用的解决方案,或多或少类似于
    addToJson(address.streetName,“blala”)
    So,对于多次使用的同一个密钥,我希望能够找到一个解决方案,该解决方案可以在应用程序中的任何地方使用,或多或少类似于
    addToJson(address.streetName,“blala”)