0
votes

I would like to write a function that transforms case classes to Json:

import play.api.libs.json._

def myJson(cc: Product): JsValue = {
  Json.toJson(cc)  // simplified
}

Each case class has an implicit Writes[T], for example:

case class Test(a: Int)
object Test {
  implicit val jsonWrites: Writes[Test] = Json.writes[Test]
}

It is possible to write Json.toJson(new Test(1)) individually, but the myJson function above does not compile because it never knows if cc has an implicit Writes defined.

[How can I write the function signature so that it takes only classes having a Writes implicit?]

Edit: How can I write the function input type so that it corresponds only to classes having a Writes implicit?

I tried this:

trait JsonWritableResult[T <: Product] {
  implicit val jsonWrites: Writes[T]
}

case class Test(a: Int)
object Test extends JsonWritableResult[Test] {
  implicit val jsonWrites: Writes[Test] = Json.writes[Test]
}

def myJson(cc: JsonWritableResult[_ <: Product]): JsValue = {
  Json.toJson(cc)
}

But it says "No Json serializer found for type models.JsonWritableResult[_$2]".

2
Why do you want your custom myJson function for arbitrary case class? How will it be different from Json.toJson? - Tyth
Simply because I have 120 such case classes and I don't want to write 120 controllers to return that many different Json results. A generic function would be best. - JulienD
@Tyth Note that it may be dumb indeed but I haven't realized it yet ) - JulienD
You simply need to accept a (implicit writes: Writes[T]) in your myJson method. What does it mean ? You are requiring the compiler to check whether the provided type has an instance of Writes - Louis F.
If I understand well, that is the signature that Json.toJson already has. What I really need is a trait/type that I can give to all results of a factory so that they are guaranteed to be valid arguments to toJson. Looks like the question was asked badly :( - JulienD

2 Answers

2
votes

Something like this seems to give you the behavior you want.

import play.api.libs.json.{JsValue, Json, Writes}

trait Product {}

case class Test(a: Int) extends Product
object Test {
  implicit val jsonWrites: Writes[Test] = Json.writes[Test]
}

def myJson[T <: Product](cc: T)(implicit writes: Writes[T]): JsValue = {
  Json.toJson(cc)  // simplified
}

import Test._

myJson(Test(3))

This is not tested in the general case, but in a Worksheet it seems to work.

0
votes

Instead of forcing the case class to have the implicit, it is more clever to force it to override a toJson method from a trait, using or not an implicit defined in the case class. Much simpler and it works. Then my factory can explicit this trait in its output type, and thus I can serialize whatever it outputs.

But since the other answer answered the question that I formulated wrong, I accept it ;)