在 playframework 2.4 中添加可选 属性 和 json 变形金刚
Add optional property with json transformers in playframework 2.4
我不明白,如何添加可选的 属性 和 json 转换器。
我想合并两个 json 对象(列表和日历),不带或带动态属性列表(例如不带 owner
):
calendar1 = {id:1, name: "first", description:"my first calendar", owner: 1}
calendar2 = {id:2, name: "second", owner: 1}
list = [{id: 1, settings: []}, {id: 2, settings: []}]
结果必须是
{calendars:
[
{id:1, name: "first", description:"my first calendar", settings: []},
{id:2, name: "second", settings: []}
]
}
开始于:
scala> case class Calendar(id:Int,name:String,description:Option[String],owner:Int)
defined class Calendar
scala> case class CalendarRow(id:Int,name:String,description:Option[String],settings:Seq[String]=Seq.empty)
defined class CalendarRow
scala> def append(calendars:Calendar*) = calendars.map(c => CalendarRow(c.id,c.name,c.description))
append: (calendars: Calendar*)Seq[CalendarRow]
scala> val calendar1 = Calendar(1,"first",Option("my first calendar"),1)
calendar1: Calendar = Calendar(1,first,Some(my first calendar),1)
scala> val calendar2 = Calendar(2, "second",None,1)
calendar2: Calendar = Calendar(2,second,None,1)
scala> val list = append(calendar1,calendar2)
list: Seq[CalendarRow] = ArrayBuffer(CalendarRow(1,first,Some(my first calendar),List()), CalendarRow(2,second,None,List()))
我假设以下 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]
。只要您的原始日历确实是 JsObject
,您就会得到一个 JsSuccess
。您可以使用以下命令验证输出结构:
calendarArray.foreach(println)
其中 returns :
{"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":[]}
]
}
非常感谢@Jean 和 comments in "Unveiling Play 2.1 Json API - Part 3 : JSON Transformers"
我很难理解 and
、andThen
、andKeep
、keepAnd
中的 JSON transformers
(我无法找到任何带有示例的详细描述),但我找到了一些模板来解决我的问题:
JSON 中的可选 属性:
和Reader
(__ \ "id").json.pick[JsString].flatMap{
case id if id.equals(JsString(accountId)) =>
(__ \ "primary").json.put(JsBoolean(true))
case _ =>
Reads.pure(Json.obj())
}
与Json.obj()
(__ \ "id").json.pick[JsString].map{
case id if id.equals(JsString(accountId)) =>
Json.obj("primary" -> true)
case _ =>
Json.obj()
}
我不明白,如何添加可选的 属性 和 json 转换器。
我想合并两个 json 对象(列表和日历),不带或带动态属性列表(例如不带 owner
):
calendar1 = {id:1, name: "first", description:"my first calendar", owner: 1}
calendar2 = {id:2, name: "second", owner: 1}
list = [{id: 1, settings: []}, {id: 2, settings: []}]
结果必须是
{calendars:
[
{id:1, name: "first", description:"my first calendar", settings: []},
{id:2, name: "second", settings: []}
]
}
开始于:
scala> case class Calendar(id:Int,name:String,description:Option[String],owner:Int)
defined class Calendar
scala> case class CalendarRow(id:Int,name:String,description:Option[String],settings:Seq[String]=Seq.empty)
defined class CalendarRow
scala> def append(calendars:Calendar*) = calendars.map(c => CalendarRow(c.id,c.name,c.description))
append: (calendars: Calendar*)Seq[CalendarRow]
scala> val calendar1 = Calendar(1,"first",Option("my first calendar"),1)
calendar1: Calendar = Calendar(1,first,Some(my first calendar),1)
scala> val calendar2 = Calendar(2, "second",None,1)
calendar2: Calendar = Calendar(2,second,None,1)
scala> val list = append(calendar1,calendar2)
list: Seq[CalendarRow] = ArrayBuffer(CalendarRow(1,first,Some(my first calendar),List()), CalendarRow(2,second,None,List()))
我假设以下 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]
。只要您的原始日历确实是 JsObject
,您就会得到一个 JsSuccess
。您可以使用以下命令验证输出结构:
calendarArray.foreach(println)
其中 returns :
{"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":[]}
]
}
非常感谢@Jean 和 comments in "Unveiling Play 2.1 Json API - Part 3 : JSON Transformers"
我很难理解 and
、andThen
、andKeep
、keepAnd
中的 JSON transformers
(我无法找到任何带有示例的详细描述),但我找到了一些模板来解决我的问题:
JSON 中的可选 属性:
和Reader
(__ \ "id").json.pick[JsString].flatMap{
case id if id.equals(JsString(accountId)) =>
(__ \ "primary").json.put(JsBoolean(true))
case _ =>
Reads.pure(Json.obj())
}
与Json.obj()
(__ \ "id").json.pick[JsString].map{
case id if id.equals(JsString(accountId)) =>
Json.obj("primary" -> true)
case _ =>
Json.obj()
}