How to modify values of JsonObject

 

问题

https://stackoverflow.com/questions/66955052/how-to-modify-values-of-jsonobject

I want to add a new field to jsonObject and this new field's name will be based on a value of another field. To be clear, this an examples of what I want to achieve.

{

"values": [

{

"id": "1",

"properties": [

{

"stat": "memory",

"data": 8

},

{

"stat": "cpu",

"data": 4

}

]

},

{

"id": "2",

"properties": [

{

"stat": "status",

"data": "OK"

},

{

"stat": "cpu",

"data": 4

}

]

}

]

}

I want to add a new field to each json object that will have the value of field "stat" as name.

{

"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

}

]

}

]

}

I have tried to do the following with JsonPath library but for me it's an ugly solution as I will parse the json three times and I do some manual replacements.

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)}")

}

This is the result obtained

{

"values": [

{

"id": "1",

"properties": [

{

"data": 8,

"memory": "8"

},

{

"data": 4,

"cpu": "4"

}

]

},

{

"id": "2",

"properties": [

{

"data": 0,

"memory": "0"

},

{

"data": 4,

"cpu": "4"

}

]

}

]

}

Is there any better method to achieve this result ? I tried to do it with gson but it's not good handling paths.

This a way to do it with Gson but I will lose the information about other columns since I'm creating another 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

}

解答

json的每个properties中的每条记录添加新字段,当前stat作为字段名,data作为字段值。用Java 实现代码较长。

Java 下的开源包 SPL 很容易写,只要 1 句:


A

1

=json(json(file("data.json").read()).values.run(properties=properties.(([["stat","data"]|stat]|[~.array()|data]).record())))

SPL 提供了 JDBC 供 Java 调用,把上面的脚本存为 addfield.splx,在 Java 中以存储过程的方式调用脚本文件:

Class.forName("com.esproc.jdbc.InternalDriver");

con= DriverManager.getConnection("jdbc:esproc:local://");

st = con.prepareCall("call addfield()");
st.execute();

SPL 源代码:https://github.com/SPLWare/esProc

问答搜集