Play Framework: How to replace all the occurrence

2019-07-18 10:58发布

Given the following JSON...

{ "id":"1234",
  "name" -> "joe",
  "tokens: [{
     "id":"1234",
     "id":"2345"
   }]
}

... I need to replace the value of all the ids by xxxx like this:

{ "id":"xxxx",
  "name" -> "joe",
  "tokens: [{
     "id":"xxxx",
     "id":"xxxx"
   }]
}

Let's start create the JSON tree:

val json = Json.obj(
  "id" -> "1234",
  "name" -> "joe",
  "tokens" -> Json.arr(
     Json.obj("id" -> "1234"),
     Json.obj("id" -> "2345")
  )
)

json: play.api.libs.json.JsObject = {"id":"1234","name":"joe","tokens":[{"id":"1234"},{"id":"2345"}]}

Then, getting all the ids is very simple:

 json \\ "id"

 res64: Seq[play.api.libs.json.JsValue] = List("1234", "1234", "2345")

Now, how do I replace the value of all the ids by xxxx?

2条回答
成全新的幸福
2楼-- · 2019-07-18 11:39

Probably it isn't most efficient way to do it, but you can try to convert your JSON to an object copy it with new fields and then convert it back to json. Unfortunately currently I don't have environment to check the code, but it should be something like this:

case class MyId(id: String)
case class MyObject(id: String, name: String, tokens: List[MyId])

implicit val idFormat = Json.format[MyId]
implicit val objectFormat = Json.format[MyObject]

val json = Json.parse(jsonString)
val jsResult = Json.fromJson[MyObject](json)

val obj = jsResult match {
      case JsSuccess(s, _) => s
      case _ => throw new IllegalStateException("Unexpected")
    }

val newObj = obj.copy(id = "xxxx")
val result = Json.toJson(newObj)
查看更多
趁早两清
3楼-- · 2019-07-18 11:51

There doesn't appear to be a nice way to do this with the standard Play JSON library, although I'd be happy to be proved wrong in that regard. You can however do it easily using the play-json-zipper extensions:

import play.api.libs.json._
import play.api.libs.json.extensions._

val json = Json.obj(
  "id" -> "1234",
  "name" -> "joe",
  "tokens" -> Json.arr(
    Json.obj("id" -> "1234"),
    Json.obj("id" -> "2345")
  )
)

// Using `updateAll` we pattern match on a path (ignoring
// the existing value, as long as it's a string) and replace it
val transformed = json.updateAll {
  case (__ \ "id", JsString(_)) => JsString("xxxx")
}

// play.api.libs.json.JsValue = {"id":"xxxx","name":"joe","tokens":[{"id":"xxxx"},{"id":"xxxx"}]}

To make that a re-usable function:

def replaceValue(json: JsValue, key: String, replacement: String) = json.updateAll {
  case (__ \ path, JsString(_)) if path == key => JsString(replacement)
}

The json-zipper extensions are still "experimental", but if you want to add them to your project add the following to your project/Build.scala appDependencies:

"play-json-zipper" %% "play-json-zipper" % "1.0"

and the following resolver:

"Mandubian repository releases" at "https://github.com/mandubian/mandubian-mvn/raw/master/releases/"
查看更多
登录 后发表回答