Scala play json - 使用相同的键更新所有值

Scala play json - update all values with the same key

假设我有一个 JsValue 的形式:

{
  "businessDetails" : {
    "name" : "Business",
    "phoneNumber" : "+44 0808 157 0192"
  },
  "employees" : [
    {
      "name" : "Employee 1",
      "phoneNumber" : "07700 900 982"
    },
    {
      "name" : "Employee 2",
      "phoneNumber" : "+44(0)151 999 2458"
    }
  ]
}

我想知道是否有一种方法可以更新属于 JsValue 中具有特定名称的键的每个值,而不考虑其复杂性? 理想情况下,我想映射每个 phone 数字,以确保删除 (0)(如果有)。 我遇到过 play-json-zipper updateAll 但是在将库添加到我的 sbt 项目时我遇到了未解决的依赖性问题。 任何帮助添加 play-json-zipper 库或在普通 play-json 中实现它都将不胜感激。 谢谢!

根据我在 play-json-zipper 项目页面中看到的内容,您可能忘记添加解析器 resolvers += "mandubian maven bintray" at "http://dl.bintray.com/mandubian/maven"

如果它没有帮助,并且您想继续自定义实现:play-json 不提供开箱即用的折叠或遍历 API over JsValue,所以它可以通过以下方式递归实现:

/**
 * JSON path from the root. Int - index in array, String - field
 */
type JsPath = Seq[Either[Int,String]]
type JsEntry = (JsPath, JsValue)
type JsTraverse = PartialFunction[JsEntry, JsValue]

implicit class JsPathOps(underlying: JsPath) {
  def isEndsWith(field: String): Boolean = underlying.lastOption.contains(Right(field))
  def isEndsWith(index: Int): Boolean = underlying.lastOption.contains(Left(index))
  def /(field: String): JsPath = underlying :+ Right(field)
  def /(index: Int): JsPath = underlying :+ Left(index)
}

implicit class JsValueOps(underlying: JsValue) {
  /**
   * Traverse underlying json based on given partially defined function `f` only on scalar values, like:
   * null, string or number.
   *
   * @param f function
   * @return updated json
   */
  def traverse(f: JsTraverse): JsValue = {
    def traverseRec(prefix: JsPath, value: JsValue): JsValue = {
      val lifted: JsValue => JsValue = value => f.lift(prefix -> value).getOrElse(value)
      value match {
        case JsNull => lifted(JsNull)
        case boolean: JsBoolean => lifted(boolean)
        case number: JsNumber => lifted(number)
        case string: JsString => lifted(string)
        case array: JsArray =>
          val updatedArray = array.value.zipWithIndex.map {
            case (arrayValue, index) => traverseRec(prefix / index, arrayValue)
          }
          JsArray(updatedArray)

        case `object`: JsObject =>
          val updatedFields = `object`.fieldSet.toSeq.map {
            case (field, fieldValue) => field -> traverseRec(prefix / field, fieldValue)
          }
          JsObject(updatedFields)
      }
    }
    traverseRec(Nil, underlying)
  }
}

可以用在下一种方式:

val json =
  s"""
     |{
     |  "businessDetails" : {
     |    "name" : "Business",
     |    "phoneNumber" : "+44(0) 0808 157 0192"
     |  },
     |  "employees" : [
     |    {
     |      "name" : "Employee 1",
     |      "phoneNumber" : "07700 900 982"
     |    },
     |    {
     |      "name" : "Employee 2",
     |      "phoneNumber" : "+44(0)151 999 2458"
     |    }
     |  ]
     |}
     |""".stripMargin

val updated = Json.parse(json).traverse {
  case (path, JsString(phone)) if path.isEndsWith("phoneNumber") => JsString(phone.replace("(0)", ""))
}

println(Json.prettyPrint(updated))

这将产生预期的结果:

{
  "businessDetails" : {
    "name" : "Business",
    "phoneNumber" : "+44 0808 157 0192"
  },
  "employees" : [ {
    "name" : "Employee 1",
    "phoneNumber" : "07700 900 982"
  }, {
    "name" : "Employee 2",
    "phoneNumber" : "+44151 999 2458"
  } ]
}

希望对您有所帮助!