【问题标题】:Add optional property with json transformers in playframework 2.4在 playframework 2.4 中添加带有 json 转换器的可选属性
【发布时间】:2015-12-11 17:33:24
【问题描述】:

我不明白,如何使用 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 scala playframework-2.0 transform


    【解决方案1】:

    我将假设以下 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}""")
    

    您需要为每个日历添加设置,然后删除所有者(如果存在)。

    将值放入分支settingsexplained 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.functionalplay.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":[]}
      ]
    }
    

    【讨论】:

      【解决方案2】:

      开始于:

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

      【讨论】:

      • 我想在没有帮助案例类的情况下使用 transform 更改 json。对于案例类,我有 json->pojo->json。我想试试coast-to-coast design
      【解决方案3】:

      非常感谢@Jean 和comments in "Unveiling Play 2.1 Json API - Part 3 : JSON Transformers"

      对我来说,在 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()
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-11
        • 2017-04-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多