使用“writes”无法将通用的case class 转换为JSON

9

我有一个类,希望能够将其转换为 JSON 格式:

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

object Page {

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

错误信息是:No apply function found matching unapply parameters

你好,你找到有效的解决方案了吗?我也遇到了同样的问题。 - Mahdi
4个回答

10
您可以像这样为通用的case class Page[T] 定义Format[Page[T]]

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

尽管这种解决方案需要更多的输入,但它可以使您的案例类 Page[T] 不受隐式参数列表或需要定义 Page[T] 的具体子类的影响,从而使其更加清晰易懂。

谢谢,这很有帮助!您能否解释一下 unlift(Page.unapply) 的具体作用是什么? - roman-roman
这似乎不起作用,我得到了这个编译错误:多态表达式无法实例化为预期类型 - codeCruncher

5

我更喜欢使用 这个解决方案,它使用了trait。但是如果您确实想让您的案例类成为通用类型,可以使用以下两种方法之一。

如果您不必使用 Page[_],也就是说,您总是会对 Page[Int]Seq[Page[String]] 调用 toJson,但不是对Page[_]Seq[Page[_]] 进行调用:

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

如果您需要对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)
    }
  }
}

如果是第二种情况,我应该从哪里获取implicit val tWrites: Writes[T]以提供给Page(...)呢? - Incerteza
@Alex:编译器会为您找到它。您只需使用Page(Seq("abc"), 0, 1, 1),编译器将重写为Page(Seq("abc"), 0, 1, 1)(Writes.StringWrites)。对象Writes始终在搜索范围内,用于隐式类型为Writes[T]的值。 - senia
1
第二种方法在val writes = Json.writes[Page[T]]处引起了相同的错误。 - Incerteza
1
and the first one also. - Incerteza
@Alex:我猜这是play的一个bug。我会尝试修复它。 - senia
1
@Alex:这个解决方案不起作用——Json.writes的实现还没有固定。 - senia

2

我认为你无法为任何类型参数拥有通用的写入器。 我提议如下:

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

1
好的,这是很多重复。我有很多“模型”,每个模型都有一个“def all(...):Page[ModelClass]”函数。 - Incerteza

0

也许你可以写一些类似这样的东西:

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

将 JsValue 替换 Seq[T]。
以下是我的代码示例:
    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")
      }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接