Providing implicit value for singletons in Play Json library

835 views Asked by At

I have following configuration:

sealed trait Status
case object Edited extends Status
case object NotEdited extends Status

case class Tweet(content:String, status:Status)

I want to use Play Json format, so I guess I have to have something like this(I don't want to do it in companion object):

trait JsonImpl{
    implicit val TweetFormat = Json.format[Tweet]
    implicit val statusFormat = Json.format[Status]
    implicit val StatusFormat = Json.format[Edited.type]
    implicit val NotEditedFormat = Json.format[NotEdited.type]
}

but compiler complains and says:

No implicit format for Tweet available.

Also it says I cannot use Edited.type because it needs apply and unapply functions. What should I do?

Edit1:

I can think of something like this:

implicit object StatusFormat extends Format[Status] {
    def reads(json: JsValue) =
      (json \ "type").get.as[String] match {
        case "" => Edited
        case _ => UnEdited
      }

    def writes(stat: Status) = JsObject(Seq(
      stat match {
        case Edited => "type" -> JsString("Edited")
        case NotEdited => "type" -> JsString("UnEdited")
      }
    ))
  }

but the read part has problem, the compiler complains that it needs JsonResult not Edited.type

2

There are 2 answers

0
Omid On BEST ANSWER

For doing that I should define an implicit object like this:

  implicit object StatusFormat extends Format[Status] {
    def reads(json: JsValue) =
      json match {
        case JsString("Edited") => JsSuccess(Edited)
        case JsString("NotEdited") => JsSuccess(NotEdited)
        case _ => JsError("cannot parse it")
      }

    def writes(stat: Status) = JsString(stat.toString)
}
2
Travis Brown On

It's also possible to do this pretty cleanly with the functional API:

import play.api.data.validation.ValidationError
import play.api.libs.functional.syntax._
import play.api.libs.json._

implicit val StatusFormat: Format[Status] = Format(
  (__ \ 'type).read[String].collect[Status](ValidationError("Unknown status")) {
    case "UnEdited" => NotEdited
    case "Edited" => Edited
  },
  (__ \ 'type).write[String].contramap {
     case Edited => "Edited"
     case NotEdited => "UnEdited"
  }
)

implicit val TweetFormat: Format[Tweet] = Json.format[Tweet]

I find this clearer than implementing reads and writes methods by hand, mostly because it highlights the symmetry between encoding and decoding. It's a matter of taste, though.