1

I run through the JSON documentation at http://www.playframework.com/documentation/2.2.x/ScalaJsonRequests but didn't find what I needed. What I need is make my class being able to be converted to JSON like this:

# model
case class MyClass(a: Int, b: String ....)

# controller

def myAction = Action {
  val myClass = getMyClass()
  Ok(toJson(myClass))
}

So wherever I call Ok(toJson(myClass)), it converts to JSON by itself. How can I do this?

P.S. Sorry, I forgot to mention MyClass has java.util.UUID as an Id and some other class as a field:

case class MyClass(id: UUID, a: Int, b: String, c: MyClass2 ....)

So Json.writes[MyClass] doesn't work at least because of UUID.

アレックス
  • 24,309
  • 37
  • 129
  • 229

2 Answers2

5

You could add a macro-generated Writes[MyClass] to companion object of MyClass like this:

object MyClass {
  implicit val myClassWrites = Json.writes[MyClass]
}

Method toJson implicitly accepts parameter of type Writes[T]. Compiler tries to find such implicit value and companion object of MyClass is always in scope when type parameter T is MyClass, so you don't have to import myClassWrites manually.

See also Where does Scala look for implicits?.

Json.writes requires implicit Writes for all constructor parameters.

case class MyClass(id: UUID, a: Int, b: String, c: MyClass2)

If MyClass2 is a case class you should create Writes[MyClass2] using Json.writes in its companion object.

For java.util.UUID you should create Writes manually, for instance:

implicit val uuidWrites: Writes[UUID] = Writes{ uuid => JsString(uuid.toString) }

You could create uuidWrites in some helper object and than import when you need it like this:

object MyClass {
  import UuidHelper.uuidWrites 
  // note that you don't have to import MyClass2.myClass2Writes here
  implicit val myClassWrites = Json.writes[MyClass]
}
Community
  • 1
  • 1
senia
  • 36,859
  • 4
  • 83
  • 123
2

You need to provide an implicit val with Writes[MyClass] (or Format) description. Play2.0.x:

import play.api.libs.json._

implicit val myClassWrites = new Writes[MyClass] {
  def writes(c: MyClass): JsValue = {
    Json.obj(
        "a" -> c.a,
        "b" -> c.b,
         ...
    )
  }
}

Play2.1.x+

implicit val myClassWrites = (
  (__ \ "a").write[String] ~
  (__ \ "b").write[Int] ~
  ...
)(unlift(MyClass.unapply))

More docs: http://www.playframework.com/documentation/2.2.x/ScalaJsonCombinators

But as senia mentioned, in a new versions of Play! you can use macro generators of Writes/Reads. Docs: http://www.playframework.com/documentation/2.2.x/ScalaJsonInception

implicit val myClassFormat = Json.format[MyClass]

This will provide you both Reads and Writes.

Update: As you have updated your answer with additional class UUID, try to provide separate Writes or Format for UUID class. It won't work with macros, as it is from java.util package, so you should provide an custom Writes, like a mentioned before.

psisoyev
  • 1,918
  • 1
  • 22
  • 34