gson 如何修改JsonObject得值

inn6fuwd  于 2022-11-06  发布在  其他
关注(0)|答案(5)|浏览(247)

我想给jsonObject添加一个新字段,这个新字段的名称将基于另一个字段的值。为了清楚起见,这是我想实现的一个示例。

{
  "values": [
    {
      "id": "1",
      "properties": [
        {
          "stat": "memory",
          "data": 8
        },
        {
          "stat": "cpu",
          "data": 4
        }
      ]
    },
    {
      "id": "2",
      "properties": [
        {
          "stat": "status",
          "data": "OK"
        },
        {
          "stat": "cpu",
          "data": 4
        }
      ]
    }
  ]
}

我想给每个json对象添加一个新的字段,该字段的值为“stat”作为名称。

{
  "values": [
    {
      "id": "1",
      "properties": [
        {
          "stat": "memory",
          "data": 8,
          "memory": 8
        },
        {
          "stat": "cpu",
          "data": 4,
          "cpu": 4
        }
      ]
    },
    {
      "id": "2",
      "properties": [
        {
          "stat": "status",
          "data": 0,
          "status": 0
        },
        {
          "stat": "cpu",
          "data": 4,
          "cpu": 4
        }
      ]
    }
  ]
}

我已经尝试用JsonPath库做了以下事情,但对我来说,这是一个丑陋的解决方案,因为我将解析json三次,我做了一些手动替换。

val configuration = Configuration.builder().options(Option.DEFAULT_PATH_LEAF_TO_NULL, Option.ALWAYS_RETURN_LIST).build()
val jsonContext5 = JsonPath.using(configuration).parse(jsonStr)
val listData = jsonContext.read("$['values'][*]['properties'][*]['data']").toString
      .replace("[", "").replace("]", "").split(",").toList
val listStat = jsonContext.read("$['values'][*]['properties'][*]['stat']").toString
      .replace("[", "").replace("]", "")
      .replace("\"", "").split(",").toList
// Replacing values of "stat" by values of "data"
jsonContext5.map("$['values'][*]['properties'][*]['stat']", new MapFunction() {
      var count = - 1
      override def map(currentValue: Any, configuration: Configuration): AnyRef = {
        count += 1
        listData(count)
      }
    })
// replace field stat by its value
for( count <- 0 to listStat.size - 1){
     val path = s"['values'][*]['properties'][$count]"
     jsonContext5.renameKey(path, "stat", s"${listStat(count)}")
}

这是得到的结果

{
  "values": [
    {
      "id": "1",
      "properties": [
        {
          "data": 8,
          "memory": "8"
        },
        {
          "data": 4,
          "cpu": "4"
        }
      ]
    },
    {
      "id": "2",
      "properties": [
        {
          "data": 0,
          "memory": "0"
        },
        {
          "data": 4,
          "cpu": "4"
        }
      ]
    }
  ]
}

有没有更好的方法来达到这个效果?我试着用gson来做,但是它处理路径不好。
这是使用Gson的一种方法,但是我将丢失关于其他列的信息,因为我正在创建另一个json。

val jsonArray = jsonObject.get("properties").getAsJsonArray
val iter = jsonArray.iterator()
val agreedJson = new JsonArray()
while(iter.hasNext) {
    val json = iter.next().getAsJsonObject
    agreedJson.add(replaceCols(json))
}
def replaceCols(json: JsonObject) = {
    val fieldName = "stat"
    if(json.has(fieldName)) {
      val columnName = json.get(fieldName).getAsString
      val value: String = if (json.has("data")) json.get("data").getAsString else ""
      json.addProperty(columnName, value)
    }
    json
}
ebdffaop

ebdffaop1#

像这样的怎么样?

private static void statDup(final JSONObject o) {
    if (o.containsKey("properties")) {
        final JSONArray a = (JSONArray) o.get("properties");
        for (final Object e : a) {
            final JSONObject p = (JSONObject) e;
            p.put(p.get("stat"), p.get("data"));
        }
    } else {
        for (final Object key : o.keySet()) {
            final Object value = o.get(key);
            if (value instanceof JSONArray) {
                for (final Object e : (JSONArray) value) {
                    statDup((JSONObject) e);
                }
            }
        }
    }
}
nkhmeac6

nkhmeac62#

使用Gson,您应该做的是创建一个表示初始JSON对象的基类。然后,将JSON对象加载到内存中,可以逐个加载,也可以全部加载,然后对每个对象进行必要的更改以包含您的更改。然后,如果在上一步中没有将这些更改Map到新类,则将其Map到新类,并将它们序列化到文件或其他存储中。

6kkfgxo0

6kkfgxo03#

这是一个类型安全的纯FP circe实现,使用circe-optics

object CirceOptics extends App {
  import cats.Applicative
  import cats.implicits._
  import io.circe.{Error => _, _}
  import io.circe.syntax._
  import io.circe.parser._
  import io.circe.optics.JsonPath._

  val jsonStr: String = ???

  def getStat(json: Json): Either[Error, String] =
    root.stat.string.getOption(json)
      .toRight(new Error(s"Missing stat of string type in $json"))

  def getData(json: Json): Either[Error, Json] =
    root.data.json.getOption(json)
      .toRight(new Error(s"Missing data of json type in $json"))

  def setField(json: Json, key: String, value: Json) =
    root.at(key).setOption(Some(value))(json)
      .toRight(new Error(s"Unable to set $key -> $value to $json"))

  def modifyAllPropertiesOfAllValuesWith[F[_]: Applicative](f: Json => F[Json])(json: Json): F[Json] =
    root.values.each.properties.each.json.modifyF(f)(json)

  val res = for {
    json          <-  parse(jsonStr)
    modifiedJson  <-  modifyAllPropertiesOfAllValuesWith { j =>
      for {
        stat  <-  getStat(j)
        data  <-  getData(j)
        prop  <-  setField(j, stat, data)
      } yield prop
    } (json)
  } yield modifiedJson

  println(res)
}
xurqigkl

xurqigkl4#

Gene McCulley的前一个答案给出了一个使用Java和类net.minidev.json的解决方案,这个答案使用类Gson,用Scala编写。

def statDup(o: JsonObject): JsonObject = {
    if (o.has("properties")) {
      val a = o.get("properties").getAsJsonArray
      a.foreach { e =>
        val p = e.getAsJsonObject
        p.add(p.get("stat").getAsString, p.get("data"))
      }
    } else {
      o.keySet.foreach { key =>
        o.get(key) match {
          case jsonArr: JsonArray =>
            jsonArr.foreach { e =>
              statDup(e.getAsJsonObject)
            }
        }
      }
    }
    o
  }
xkftehaa

xkftehaa5#

您的任务是在JSON文件的每个属性下为每条记录添加一个新字段,使当前的stat值成为字段名,数据值成为新字段值。如果尝试用Java来做,代码会相当长。
建议你使用SPL,一个开源的Java包来完成它。编码将非常容易,你只需要一行:
| | A级|
| - -|- -|
| 一个|=json(json(file(“数据. json”).read()).值.运行(属性=属性.(([[“统计”,“数据”]|统计]|[~.数组()|数据]).record())))|
SPL提供了Java调用的JDBC驱动程序,只需将上面的SPL脚本存储为addfield.splx,在Java应用程序中调用存储过程时调用它即可:

…
Class.forName("com.esproc.jdbc.InternalDriver");
con= DriverManager.getConnection("jdbc:esproc:local://");
st = con.prepareCall("call addfield()");
st.execute();
…

相关问题