我将假设以下 json 树
val calendar1 = Json.parse("""{"id":1, "name": "first", "description":"my first calendar", "owner": 1}""")
val calendar2 = Json.parse("""{"id":2, "name": "second", "owner": 1}""")
您需要为每个日历添加设置,然后删除所有者(如果存在)。
将值放入分支settings 是explained in the documentation
val addSettings = __.json.update((__ \ "settings").json.put(Json.arr()))
掉主人也是explained
val removeOwner = (__ \ "owner").json.prune
现在您可以定义要应用于每个日历对象的转换器
val transformer = addSettings andThen removeOwner
根据您的数据的实际建模方式,有多种选择。如果您有 Seq 的日历,如
val calendars = Seq(calendar1, calendar2)
你可以的
val normalizedCalendars = calendars.map(_.transform(transformer))
这会给你一个Seq[JsResult[JsObject]],你想把它转换成一个JsResult[Seq[JsObject]]。
我很确定有一种方法可以使用 play 的函数语法(参见 play.api.libs.functional 和 play.api.libs.functional.syntax),但是这部分 play 没有很好的记录,我还没有开始研究 Applicatives尽管我对他们的所作所为有所了解。
相反,我依赖以下受 scala 的 Future#sequence 启发的代码
def sequence[A, M[X] <: TraversableOnce[X]](in: M[JsResult[A]])(implicit cbf: CanBuildFrom[M[JsResult[A]], A, M[A]]): JsResult[M[A]] = {
val empty: JsResult[mutable.Builder[A, M[A]]] = JsSuccess(cbf(in))
in.foldLeft(empty) {(jracc,jrel) => (jracc,jrel) match {
case (JsSuccess(builder,_), JsSuccess(a,p)) =>JsSuccess(builder+=a, p)
case (ra@JsError(builderErrors), re@JsError(errors)) =>JsError.merge(ra, re)
case (JsSuccess(_,_), re@JsError(errors)) => re
case (ra@JsError(builderErrors), JsSuccess(_,_)) => ra
}} map (_.result())
}
你可以这样写:
val calendarArray = sequence(normalizedCalendars).map(v=>Json.obj("calendars"->JsArray(v)))
这会给你一个JsResult[JsObject]。只要您的原始日历确实是JsObjects,您就会得到JsSuccess。您可以使用以下命令验证输出结构:
calendarArray.foreach(println)
返回:
{"calendars":[{"id":1,"name":"first","description":"my first calendar","settings":[]},{"id":2,"name":"second","settings":[]}]}
这与您要求的以空格为模的问题相同
{
"calendars":[
{"id":1,"name":"first","description":"my first calendar","settings":[]},
{"id":2,"name":"second","settings":[]}
]
}