Scala JSON写入计算值

问题描述 投票:0回答:1

我们是用Play的JSON序列化来序列化一个模型。

case class Foo(
    var id: Long,
    var birthday: Date,
    [...]
)
object Foo {
  implicit val fooFormat: OFormat[Foo] = Json.format[Foo]
}

然而我们想添加一些计算值,例如。

case class Foo(
    ...
) {
    def age: Int = 32
}

为了在序列化中包含年龄,似乎我们必须通过写出一个完整的Writesunapply方法来重复所有的字段名。

implicit val fooWrites: Writes[Foo] = (
    (JsPath \ "id").write[Long] and
    (JsPath \ "birthday").write[Date] and
    [...]
    (JsPath \ "age").write[Int]
)(unlift(Foo.unapply_extended))

def unapply_extended(a: Foo): Option[(Long,Date,[...],Int)] = Some(( a.id, a.birthday, [...], a.age))

implicit val fooReads = Json.reads[Foo]

有没有一种方法可以在JSON序列化中包含计算出的年龄值,而不需要多次重新列举相同的case类fieldstypes? 当有很多字段时,很快就会变得无法管理,所有的信息都是多余的。

scala playframework
1个回答
2
投票

我不认为这是不可能的,因为生成的编解码器是针对普通用例(case类及其字段)进行优化的。但你可以推导出编解码器,并在事后通过使用 变压器.

case class Foo(
    ...
) {
    def age: Int = 32
}
object Foo {
  implicit val fooFormat: OFormat[Foo] = {
    val derived = Json.format[Foo] // majority of work done here

    // here we just modify output of writer to add that one field
    OFormat(
      r = derived,
      w = foo => Json.writes[Foo].transform { json: JsObject =>
        json.transform((__ \ 'age).json.put(JsNumber(foo.age))).getOrElse(json)
      }.writes(foo)
    )
  }
}

如果你分别推导出Reads和Writes,你可以节省一两行。

object Foo {
  implicit val fooFormat: OFormat[Foo] = OFormat(
    r = Json.reads[Foo],
    w = foo => Json.writes[Foo].transform { json: JsObject =>
      json.transform((__ \ 'age).json.put(JsNumber(foo.age))).getOrElse(json)
    }.writes(foo)
  )
}
© www.soinside.com 2019 - 2024. All rights reserved.