在Scala中动态遍历和更新JSON值



我需要使用Scala动态更新JSON值。我不会事先知道JSON的结构,它会随着用户的不同而变化。用户将从某个文件输入JSON,并提供需要更新嵌套形式键的值。为了更新值,我会调用一个库,为了简单起见,我想反转这些值。

输入JSON(可以是任何JSON(:

{
"emp_data": [{
"employee": {
"name": "sonoo",
"salary": 56000,
"desig": "Manager"
}
},
{
"employee": {
"name": "monoo",
"salary": 66000,
"desig": "Engineer"
}
}
],
"version:": 10.1,
"Dept": "HR",
"contact": {
"email": "sean@3dx.com",
"phone": ["+1 9876543210", "+1 8976543210"]
}
}

需要更新的密钥:emp_data.employee.name, contact.phone(可以是基于输入JSON的任何密钥(

我需要的输出是:

{
"emp_data": [{
"employee": {
"name": "oonos",
"salary": 56000,
"desig": "Manager"
}
},
{
"employee": {
"name": "oonom",
"salary": 66000,
"desig": "Engineer"
}
}
],
"version:": 10.1,
"Dept": "HR",
"contact": {
"email": "sean@3dx.com",
"phone": ["0123456789 1+", "0123456798"]
}
}

请建议一些有效的方法

这里有一个草图,可以让您了解如何实现它。它需要循环。代码不完整,不安全,可以在许多方面进行优化(例如,避免在没有修改的情况下重新创建json(。它只包括emp_data.employee.name密钥,而不包括contact.phone密钥。如果你想涵盖所有边缘情况,你需要在设计路径的规则上付出一些努力,否则代码会变得非常复杂(例如,当我已经遵循完整的路径时,我怎么知道你希望进入一个数组并修改它的元素(。

import io.circe._, io.circe.parser._
// Recursively traversing the json along the given path.
def findAndReplace(in: Json, path: List[String], f: Json => Option[Json]): Json = {
(path match {
case h :: Nil =>
// the transformation function f is applied only to objects' fields
in.asObject.filter(_.contains(h)).flatMap { obj =>
obj(h).flatMap(f).map { newVal => Json.fromJsonObject(obj.add(h, newVal)) }
}
case h :: remainingPath if in.isArray =>
// if it's an array then we get inside and leave the path unchanged
in.asArray.filter(_.nonEmpty).map { items =>
Json.fromValues(items.map( item => findAndReplace(item, path, f) ))
}
case h :: remainingPath if in.isObject =>
// the object has to have a field with name as the head of the path to continue traverse
in.asObject.flatMap { obj =>
obj(h).map { oldVal =>
val newVal = findAndReplace(oldVal, remainingPath, f)
Json.fromJsonObject(obj.add(h, newVal))
}
}
// stop traversing for other cases
case _ => None
}).getOrElse(in)
}
// Function for applying the same function f to multiple paths 
def findAndReplaceAll(in: Json, paths: List[List[String]], f: Json => Option[Json]): Json = {
// we go through whole json for each path
paths.foldLeft(in) { case (newJson, path) =>
findAndReplace(newJson, path, f)
}
}

val json = parse(input).toOption.get
val newJson = findAndReplaceAll(
json,
List(List("emp_data", "employee", "name"), List("contact", "phone")),
{ _.asString.map(_.reverse).map(Json.fromString) }
)
println(json.noSpaces)
println(newJson.noSpaces)

最新更新