Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatic serialization/deserialization of generic case classes to/from JSON in Play2.2 Scala

This question is based on the following example, which is an attempt to deserialize a case class Node[Bird] from JSON.

import play.api.libs.json._
import play.api.libs.functional.syntax._

object ValidationTest {

  case class Node[T](_id: Int, thing: T)

  case class Bird(name: String, color: String)

  // ERROR -- No apply function found matching unapply parameters
  implicit val birdNodeFormat = Json.format[Node[Bird]]

  val birdNodesJson: JsValue = Json.arr(
    Json.obj(
      "_id" -> 0,
        "thing" -> Json.obj(
          "name" -> "Cardinal",
          "color" -> "Red"
      )
    ),
    Json.obj(
      "_id" -> 1,
      "thing" -> Json.obj(
        "name" -> "Bluejay",
        "color" -> "Blue"
      )
    )
  )

  val birdNodesResult = birdNodesJson.validate[Seq[Node[Bird]]]

}

In the preceding example, Scala is unable to resolve the proper apply/unapply functions for Node[Bird] for the format macro.

  // ERROR -- No apply function found matching unapply parameters
  implicit val birdNodeFormat = Json.format[Node[Bird]]

However, there is no problem with using a non-generic case class such as BirdNode.

  case class BirdNode(_id: Int, thing: Bird)

  // WORKS
  implicit val birdNodeFormat = Json.format[BirdNode]

  ...

  // WORKS
  val birdNodesResult = birdNodesJson.validate[Seq[BirdNode]]

What is the best way to serialize/deserialize something like a Node[Bird] to/from JSON in Play 2.2?

like image 253
johnh Avatar asked Nov 11 '22 18:11

johnh


1 Answers

You might have to define the format for Node[T] without using the macro, but this works:

implicit val birdFormat: Format[Bird] = Json.format[Bird]

implicit def nodeFormat[T : Format]: Format[Node[T]] =
  ((__ \ "_id").format[Int] ~
    (__ \ "thing").format[T]
  )(Node.apply, unlift(Node.unapply))
like image 98
Ben James Avatar answered Nov 15 '22 07:11

Ben James