繁体   English   中英

在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: []}
    ]
}

我假设以下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放置值的方法

val addSettings = __.json.update((__ \ "settings").json.put(Json.arr()))

说明了放弃所有者

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.functionalplay.api.libs.functional.syntax ),但是这部分内容并没有得到很好的记录,我还没有得到尽管我对他们的工作有所了解,但仍然可以学习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)

返回:

{"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":[]}
  ]
}

从...开始:

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()))

非常感谢@Jean和“发布Play 2.1 Json API-第3部分:JSON变形器”中的评论

我很难理解JSON transformers andandThenandKeepkeepAnd (我无法找到带有示例的详细描述),但是我发现了一些模板来回答我的问题:

JSON中的可选属性:

与读者

(__ \ "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()
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM