Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

json - Create Writes and Format of Case Class

For this case class:

case class People(names: Set[Int])

Travis Brown explained how to create PeopleReads: Reads[People] at this answer:

implicit val PeopleReads = 
       (__  "names").read[Set[Id]].map(People)

But, I'm trying to implement PeopleWrites: Writes[People]:

 implicit val PeopleWrites: Writes[People] = 
    (JsPath   "names").write[Set[Int]].map(unlift(x => Some((x.names)))

with the following compile-time error:

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

How can I resolve this error?

Also, how can I write Format[People] where I get/define both Reads and Writes:

val peopleFormat: Format[People] = ...?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Good question! The reason you can't use map is because Writes isn't a functor.

You can think of Writes[A] as something kind of like A => JsValue. But suppose I've got a A => JsValue and a A => B. Try to come up with some way of composing those functions to get a B => JsValue—it's just not possible.

Reads[A], on the other hand, is kind of like JsValue => A, and is a functor—it has a map method that takes a A => B, composes it with the Reads[A] / JsValue => A, and returns a Reads[B] / JsValue => B.

Writes is, however, a contravariant functor, and luckily enough Play knows that. When F is a contravariant functor, F[A] has a method contramap[B](f: B => A) instead of the usual map[B](f: A => B). So you can just write this:

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)

Here (__ 'names).write[Set[Int]] is a Writes[Set[Int]] and (_.names) is a function People => Set[Int]. Combining them with contramap gives us a Writes[People].


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...