Создать общую функцию сериализации Json
Можно ли создать универсальную функцию в Scala с использованием Play Framework 2.2, которая будет сериализовать произвольный объект в JSON, без необходимости предоставления средства записи или форматирования?
Например, этот неуниверсальный код создаст ответ JSON, заданный Customer:
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 ответ
Вам нужно где-то определить форматтер для каждого типа, который вы хотите прочитать или написать. Это связано с тем, что экземпляры форматера разрешаются во время компиляции, а не во время выполнения. Это хорошо, потому что это означает, что попытка сериализации типа, который не имеет сериализатора, становится ошибкой во время компиляции, а не во время выполнения.
Вместо определения форматеров на лету, определите их в модуле, который вы можете использовать повторно, например
object JsonFormatters {
implicit val customerWrites: Format[Customer] = Json.format[Customer]
}
затем import JsonFormatters._
в том объеме, который вы хотите написать JSON.
Теперь вы можете написать универсальный метод, аналогичный тому, который вы хотели: вам просто нужно указать требование для форматера в сигнатуре вашего метода. На практике это неявный параметр типа 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]
,