Scala JSON 写入计算值

Scala JSON Writes with calculated values

我们正在使用 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
}

为了在序列化中包含年龄,看来我们必须通过写出完整的 Writes/unapply 方法来复制所有字段名称:

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 序列化中,而不必多次重新枚举相同的案例 class fields/types?当有很多字段时,它很快变得难以管理,所有信息都是多余的。

开箱即用我认为这是不可能的,因为生成的编解码器针对常见用例(案例 类 及其字段)进行了优化。但是您可以使用 transformers.

派生编解码器并在之后调整它们的行为
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)
    )
  }
}

如果单独导出读取和写入,您可以节省一两行代码

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