创建一个通用的Json序列化函数

时间:2013-10-26 20:09:31

标签: json scala generics playframework playframework-2.2

是否可以使用Play Framework 2.2在Scala中创建一个通用函数,它将任意对象序列化为JSON,而不必提供编写器或格式化程序?

例如,这个非通用代码将为Customer提供一个JSON响应:

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

case class Customer(id: Int, name: String)

object scratch {
  val p = Customer(1, "n")                        
  //> p  : Customer = Customer(1,n)

  def createJsonResponseCustomer(data: Customer) = {
    implicit val formatter = Json.format[Customer]
    Json.obj("success" -> true, "data" -> Json.toJson[Customer](data))
  }

  createJsonResponseCustomer(p)                   
  //> res0: play.api.libs.json.JsObject = {"success":true,"data":{"id":1,"name":"n"}}
}

为了避免为每个不同的对象定义格式化程序,我想创建一个这样的通用函数:

def createJsonResponse[T](data: T) = {
  implicit val formatter = Json.format[T]
  Json.obj("success" -> true, "data" -> Json.toJson[T](data))
}

但是这次尝试会在No unapply function found处产生错误Json.format[T]

换句话说,这有效:

def getFormatter(c: Customer) = Json.format[Customer]

但这不是:

def getFormatterGeneric[T](c: T) = Json.format[T]

有什么方法吗?

1 个答案:

答案 0 :(得分:10)

您需要在某个位置为您希望读取或写入的每种类型定义格式化程序。这是因为格式化程序实例在编译时解析,而不是在运行时解析。 这是一件好事,因为它意味着尝试序列化没有序列化程序的类型会成为编译时错误,而不是运行时错误。

不是动态定义格式化程序,而是在可以重复使用的模块中定义它们,例如

object JsonFormatters {
  implicit val customerWrites: Format[Customer] = Json.format[Customer]
}

然后在你要写一些JSON的范围内import JsonFormatters._

现在,您可以编写一个类似于您想要的通用方法:您只需在方法的签名中指定格式化程序的要求。实际上,这是Writes[T]类型的隐式参数。

def createJsonResponse[T](data: T)(implicit writes: Writes[T]) =
  Json.obj("success" -> true, "data" -> Json.toJson[T](data))

您也可以使用上下文绑定语法编写此方法签名,即

def createJsonResponse[T : Writes](data: T) = ...

这要求范围内有Writes[T]的实例;但是编译器会根据类型T为您选择正确的实例,而不是明确地解析它。

请注意Writes[T]Format[T]的超类型;由于您只是在此方法中编写 JSON,因此无需为Format[T]指定要求,这也会为您提供Reads[T]