在Play 2.3中,我有一个带有单个可选double成员的case类:

case class SomeClass(foo: Option[Double])

我需要一个将成员处理为可空的JSON写转换器:
implicit val someClassWrite: Writes[SomeClass] = ???

Play docs provide an example:
case class DisplayName(name:String)
implicit val displayNameWrite: Writes[DisplayName] = Writes {
  (displayName: DisplayName) => JsString(displayName.name)
}

但是可悲的是,我无法弄清楚如何做到这一点:1)一个可为null的空值和2)一个double。有任何想法吗?谢谢。

更新#1:我唯一能想到的解决方案是:
implicit val someClassWrite: Writes[SomeClass] = Writes {
  (someClass: SomeClass) => someClass.foo match {
    case Some(f) => JsNumber(BigDecimal(f))
    case _ => JsNull
}

更新#2:忽略我的解决方案。特拉维斯·布朗(Travis Brown)就是其中之一。

最佳答案

Writes不是协变仿函数,因此您不能使用map,但是可以使用contramap:

import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val someClassWrites: Writes[SomeClass] =
  (__ \ 'foo).writeNullable[Double].contramap(_.foo)

如果您有多个成员,则可以使用Play的FunctionalBuilder语法:
case class AnotherClass(foo: Option[Double], bar: Option[String])

implicit val anotherClassWrites: Writes[AnotherClass] = (
  (__ \ 'foo).writeNullable[Double] and
  (__ \ 'bar).writeNullable[String]
)(ac => (ac.foo, ac.bar))

在第一种情况下,contramap的参数只是从您想要的Writes的类型到您要调用Writescontramap中的类型的函数。在第二种情况下,最后的功能是从目标(此处为AnotherClass)到使用Writes构建的and实例的元组(在本例中为Option[Double]Option[String])。

10-05 18:10