我正在尝试更新json对象中的特定字段,其结构如下所示:
{ "foo": [
{
"bar": {
"ref": "ref to ignore",
"baz": {
"ref": "my_old_value"
}}}]}
我正在使用Jackson将Json4S解析为该对象,并想创建一个新的json对象,并将此特定“ ref”字段的值更改为其他值。我可以显示当前的ref对象:
[scala> json.\("foo")(0).\("bar").\("baz")
res6: JObject(List((ref,JString(my_old_value))))
我能够使用transform / transformField函数生成一个新对象,我将保留转换的详细信息:
[scala> val transformed = json.\("foo")(0).\("bar").\("baz").transform { ..
transformed: JObject(List((ref,JString(my_new_value))))
我似乎无法找出创建新对象的正确方法,该方法是将他的原始“ ref”对象替换为整个json对象中已转换的“ ref”对象。我还需要注意的是,在我实际的json对象中,有很多“ ref”对象,我只需要更新位于json.\("foo")(0).\("bar").\("baz").("ref")
我尝试同时使用replace和flatMap函数来执行此操作,但是无法使其正常工作。谁能提供一些关于如何使用Json4S的建议?
谢谢
答案 0 :(得分:1)
与Scala中的大多数库一样,json4s具有不变性。无法更改JValue中的值,但是可以使用更改创建一个新值(例如case类复制)。
如果要更改每个“ ref”字段,可以使用具有模式匹配的mapField。
import org.json4s._
import org.json4s.native.JsonMethods._
val str = """{ "foo": [
{
"bar": {
"baz": {
"ref": "my_old_value"
}}}]}"""
val json = parse(str)
val updated = json.mapField {
case ("foo", JArray(head :: tail)) => ("foo", JArray(head.mapField {
case ("ref", JString("my_old_value")) => ("ref", JString("new value"))
case otherwise => otherwise
} :: tail))
case otherwise => otherwise
}
println(updated)
// JObject(List((foo,JArray(List(JObject(List((bar,JObject(List((baz,JObject(List((ref,JString(new value)))))))))))))))
编辑
我修改了replace方法以增加对数组的支持,现在您可以通过“ foo []”或特定元素“ foo [index]”来修改数组中的所有元素。在您的范围内添加此隐式类。
implicit class JValueOps(underlying:JValue) {
object ArrayIndex {
val R = """^([^\[]+)\[(\d+)\]""".r
def unapply(str: String): Option[(String, Int)] = str match {
case R(name, index) => Option(name, index.toInt)
case _ => None
}
}
object ArrayAll {
val R = """^([^\[]+)\[\]""".r
def unapply(str: String): Option[String] = str match {
case R(name) => Option(name)
case _ => None
}
}
def replace2(l: List[String], replacement: JValue): JValue = {
def rep(l: List[String], in: JValue): JValue = {
(l, in) match {
// eg "foo[0]"
case (ArrayIndex(name, index) :: Nil, JObject(fields)) => JObject(
fields.map {
case JField(`name`, JArray(array)) if array.length > index => JField(name, JArray(array.updated(index, replacement)))
case field => field
}
)
// eg "foo[0]" "bar"
case (ArrayIndex(name, index) :: xs, JObject(fields)) => JObject(
fields.map {
case JField(`name`, JArray(array)) if array.length > index => JField(name, JArray(array.updated(index, rep(xs, array(index)))))
case field => field
}
)
// eg "foo[]"
case (ArrayAll(name) :: Nil, JObject(fields)) => JObject(
fields.map {
case JField(`name`, JArray(array)) => JField(name, JArray(array.map(_ => replacement)))
case field => field
}
)
// eg "foo[]" "bar"
case (ArrayAll(name) :: xs, JObject(fields)) => JObject(
fields.map {
case JField(`name`, JArray(array)) => JField(name, JArray(array.map( elem => rep(xs, elem))))
case field => field
}
)
// eg "foo"
case (x :: Nil, JObject(fields)) => JObject(
fields.map {
case JField(`x`, value) ⇒ JField(x, replacement)
case field ⇒ field
}
)
// eg "foo" "bar"
case (x :: xs, JObject(fields)) => JObject(
fields.map {
case JField(`x`, value) ⇒ JField(x, rep(xs, value))
case field ⇒ field
}
)
case _ => in
}
}
rep(l, underlying)
}
}
那你就可以做
json.replace2("foo[0]" :: "bar" :: "baz" :: "ref" :: Nil, JString("new value"))