Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I write and read an empty case class with play-json?

I have an empty case class corresponding to an HTTP GET request:

case class GetFoo() extends MyQueryRequest {
  // ...
}

and each message has a companion object with describes its implicit JSON writer and reader:

object GetFoo extends MyImplicitJsonProvider[GetFoo] {
  implicit val write = Json.writes[GetFoo]
  implicit val read = Json.reads[GetFoo]
}

However, because GetFoo takes no parameters, there's no way to (de)serialize it:

Unapply of object GetFoo has no parameters. Are you using an empty case class?

A workaround to inject a dummy Boolean variable into the constructor for GetFoo, but this is a kludge. I'd like to make GetFoo (de)serializable as an empty JSON object. How can I do that?

Since GET requests don't send data, it would be even better to make the reader/writer throw an exception if it were being used since the request shouldn't need to be written or read ever, but is required by the extended class.

My design relies on the GetX class extending MyQueryRequest and and the GetX companion object extending MyImplicitJsonProvider[GetX].

like image 576
erip Avatar asked Mar 29 '17 14:03

erip


1 Answers

Throwing an error

If you just want to have non implemented implicit values, you may very well do

implicit def format: Format[GetFoo] = ???

This will add the implicit in the scope whenever you need it, but will throw a NotImplementedException if it is invoked.

Dummy serialization

If you want (de)serialization as empty JsObject, just implement it as such:

implicit val nonStrictReads = Reads.pure(GetFoo()) // does not check anything on the `JsValue`, always give a `JsSuccess(GetFoo())`
implicit val strictReads = Reads[GetFoo](json => json.validate[JsObject].filter(_.values.isEmpty).map(_ => GetFoo())
implicit val writes = OWrites[GetFoo](_ => Json.obj())

Better catch it while you can

However, it would be better to catch the error at compile time, by ensuring (through stronger typing) that your request has no content. To do this, I would need more information on MyQueryRequest and MyImplicitJsonProvider to help you, but I would imagine doing something like MyQueryRequest[NoContent] or MyQueryRequest[JsValue] depending on when you have some content or not. Then one would require JSON serializer, while the other would not.

By the way, you might want to replace your empty case class by a case object, to avoid unnecessary multiple allocations (unless you do some ).

like image 66
Cyrille Corpet Avatar answered Oct 23 '22 19:10

Cyrille Corpet