Unable to convert generic case class to json using

2019-03-13 12:48发布

问题:

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

回答1:

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].



回答2:

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)
    }
  }
}


回答3:

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]
}


回答4:

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")
      }