通过将项目添加到其包含数字数组的分支之一来更新 JsObject

Update a JsObject by adding an item to one of its branches holding an array of numbers

我有 JsObject 个动态内容,例如可能如下所示:

{
    "foo": "bar",
    "viewedTaskIds": [1, 2, 3, 4]
}

viewedTaskIds 是一个整数数组。

我想做的是更新此 JsObject 并向 viewTaskIds 添加一个新号码(假设 5)。我希望结果是 JsObject.

如何在 Play Framework Scala 应用程序 (v2.3) 中执行此操作?

编辑:如果分支 viewedTaskIds 不存在,则应使用仅包含新数字的数组创建它。

您可以使用 JSON 变压器。特别是,您想在分支上执行 update

val transformer = (__ \ "viewedTaskIds").json.update(
    __.read[JsArray].map(_.append(JsNumber(5)))
)

这将查找包含您要更新的数组的分支。 update 接受一个 Reads[A <: JsValue] 来转换值。如果成功,我们会将其读取为 JsArray,然后 map 以附加 JsNumber(5)

然后我们需要做的就是将转换器应用到 JsValue:

val js = Json.parse("""{
    "foo": "bar",
    "viewedTaskIds": [1, 2, 3, 4]
}""")

scala> js.transform(transformer)
res6: play.api.libs.json.JsResult[play.api.libs.json.JsObject] = JsSuccess({"foo":"bar","viewedTaskIds":[1,2,3,4,5]},/viewedTaskIds)

如果您想在分支有错误的地方提供一个空数组,您可以将 orElse 添加到转换器并提供 Reads.pure(JsArray()) 以确保它存在。

val transformer = (__ \ "viewedTaskIds").json.update(
    __.read[JsArray].orElse(Reads.pure(JsArray())).map(_.append(JsNumber(5)))
)

不幸的是,我很难找到一个很好的解决方案来在路径根本不存在时提供一个空数组。 orElse 不起作用,因为路径不在转换器的顶层。起作用的是提供后备变压器。它不是很漂亮,而且这种形式很粗糙,但它有效:

val fallback = __.json.update((__ \ "viewedTaskIds").json.put(JsArray(Seq(JsNumber(5)))))

scala> js.transform(transformer).orElse(js.transform(fallback))
res19: play.api.libs.json.JsResult[play.api.libs.json.JsObject] = JsSuccess({"foo":"bar","viewedTaskIds":[5]},)

如果这是一个重复操作,您应该使用 update 创建一个转换器并使用 transform 调用它。您可以阅读有关变形金刚的更多信息 here

不过,如果这是一次性操作,您可能希望对 JsObjectJsArray 使用基本操作。具体来说,您可以使用 +-JsObject 添加和删除键值对,使用 :+ 将元素附加到 JsArray,并合并 JsObjects++:

val model = Json.parse("""{ "foo": "bar", "viewedTaskIds": [1, 2, 3, 4] }""").as[JsObject]
val newIds = (model \ "viewedTaskIds").as[JsArray] :+ JsNumber(5)
val newModel = model - "viewedTaskIds" + ("viewedTaskIds" -> newIds) 
//alternative to using - and +:
val newModel = model ++ Json.obj("viewedTaskIds" -> newIds)

//one liner:
val newModel = model ++ Json.obj("viewedTaskIds" -> ((model \ "viewedTaskIds").as[Seq[Int]] :+ 5))

如评论中所述,如果 JSON 未按预期格式化,其中一些操作(例如 as[Seq[Int])将抛出异常。变形金刚更适合处理这种情况,这里的技术要谨慎使用。