对于这种情况类:
case class People(names: Set[Int])
特拉维斯·布朗(Travis Brown)解释了如何创建PeopleReads: Reads[People]
这个答案:
implicit val PeopleReads =
(__ "names").read[Set[Id]].map(People)
但是,我正在尝试实现PeopleWrites: Writes[People]
:
implicit val PeopleWrites: Writes[People] =
(JsPath "names").write[Set[Int]].map(unlift(x => Some((x.names)))
出现以下编译时错误:
scala> People( Set(1,2,3))
res5: People = People(Set(1, 2, 3))
scala> implicit val PeopleWrites: Writes[People] =
(JsPath "names").write[Set[Int]].map(unlift(x => Some((x.names))))
<console>:21: error: value map is not a member of
play.api.libs.json.OWrites[Set[Int]]
implicit val PeopleWrites: Writes[People] =
(JsPath "names").write[Set[Int]].
map(unlift(x => Some((x.names)))
如何解决此错误?
另外,我如何在获取/定义Reads
和Writes
的地方编写Format[People]
:
val peopleFormat: Format[People] = ...
?
好问题!不能使用map
的原因是因为Writes
不是函子。
你可以把Writes[A]
想象成有点像A => JsValue
的东西。但是假设我有一个A => JsValue
和一个A => B
.尝试想出一些方法来组合这些函数以获得B => JsValue
- 这是不可能的。
另一方面,Reads[A]
有点像JsValue => A
,并且是一个函子——它有一个map
方法,它接受一个A => B
,用Reads[A]
/JsValue => A
组成它,并返回一个Reads[B]
/JsValue => B
。
然而,Writes
是一个逆变函子,幸运的是,Play知道这一点。当F
是逆变函子时,F[A]
有一个方法contramap[B](f: B => A)
而不是通常的map[B](f: A => B)
。所以你可以写这个:
case class People(names: Set[Int])
import play.api.libs.json._
import play.api.libs.functional.syntax._
implicit val PeopleWrites: Writes[People] =
(__ 'names).write[Set[Int]].contramap(_.names)
这里(__ 'names).write[Set[Int]]
是一个Writes[Set[Int]]
,(_.names)
是一个函数People => Set[Int]
。将它们与contramap
相结合会给我们带来Writes[People]
.