Incerteza
Incerteza

Reputation: 34884

Unable to convert generic case class to json using "writes"

I have a class which I want to be able to convert to json:

case class Page[T](items: Seq[T], pageIndex: Int, pageSize: Int, totalCount: Long)

object Page {

  implicit val jsonWriter: Writes[Page[_]] = Json.writes[Page[_]]
}

The error is No apply function found matching unapply parameters

Upvotes: 9

Views: 5274

Answers (4)

Josef Vlach
Josef Vlach

Reputation: 379

You can define Format[Page[T]] for generic case class Page[T] like this:

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

implicit def pageFormat[T: Format]: Format[Page[T]] =
  ((__ \ "items").format[Seq[T]] ~
    (__ \ "pageIndex").format[Int] ~
    (__ \ "pageSize").format[Int] ~
    (__ \ "totalCount").format[Long])(Page.apply, unlift(Page.unapply))

Although this solution requires more typing, it keeps your case class Page[T] clear of implicit parameter list or need to define concrete subclasses of Page[T].

Upvotes: 10

fairjm
fairjm

Reputation: 1187

maybe you can write something like:

    case class Page(items: JsValue, pageIndex: Int, pageSize: Int, totalCount: Long)

Put the JsValue instead of the Seq[T]

here is my code example:

    case class ResponseObject(state:Int = 1,data:JsValue)

    case class City(id:Int,name:String,sort:String){
      require(id > 0)
    }

      def list = Action {
        implicit val cityFormat:Format[City] = Json.format[City]
        implicit val responseFormat=Json.format[ResponseObject]
        Ok(Json.toJson(ResponseObject(data=Json.toJson(City.list)))).as("application/json")
      }

Upvotes: 0

senia
senia

Reputation: 38045

I'd prefer this solution with trait, but in case you do want to make your case class generic you could use one of 2 approaches.

In case you don't have to use Page[_], i.e. you'll always call toJson on Page[Int] or Seq[Page[String]], but not on Page[_] or Seq[Page[_]]:

object Page {
  implicit def pageWriter[T: Writes](): Writes[Page[T]] = Json.writes[Page[T]]
}

In case you have to serialize Page[_]:

case class Page[T](items: Seq[T],
                   pageIndex: Int,
                   pageSize: Int,
                   totalCount: Long)(
      implicit val tWrites: Writes[T])

object Page {
  implicit def pageWriter[T]: Writes[Page[T]] = new Writes[Page[T]] {
    def writes(o: Page[T]): JsValue = {
      implicit val tWrites = o.tWrites
      val writes = Json.writes[Page[T]]
      writes.writes(o)
    }
  }
}

Upvotes: 5

Rado Buransky
Rado Buransky

Reputation: 3294

I don't think that you can have a generic writer for any type parameter. I propose following:

trait Page[T] {
  val items: Seq[T]
  val pageIndex: Int
  val pageSize: Int
  val totalCount: Long
}

case class IntPage(items: Seq[Int], pageIndex: Int, pageSize: Int, totalCount: Long) extends Page[Int]

object Page {
  implicit def jsonWriter = Json.writes[IntPage]
}

Upvotes: 2

Related Questions