2013-10-26 1 views
4

Возможно ли создать общую функцию в Scala с помощью Play Framework 2.2, которая будет сериализовать произвольный объект в JSON без необходимости предоставления писателя или форматирования?Создать универсальную функцию сериализации Json

Например, это не-универсальный код будет создать ответ 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] 

Есть ли способ обойти это?

ответ

9

Вам необходимо определить форматтер где-нибудь, для каждого типа, который вы хотите читать или писать. Это связано с тем, что экземпляры форматирования разрешаются во время компиляции, а не во время выполнения. Это хорошая вещь, потому что это означает, что попытка сериализации типа, который не имеет сериализатора, становится ошибкой времени компиляции, а не временем выполнения.

Вместо определения форматов на лету, определите их в модуле, который можно повторно использовать, например.

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

+1

Возможно, было бы неплохо добавить, что вы можете просто создать 'Writes [T]' автоматически: 'implicit blahWrites = Json.Writes [Blah]' ([ссылка на docs] (http://www.playframework.com /documentation/2.2.x/ScalaJsonInception)). – Carsten

+3

Да, причина, по которой вы не можете сделать это раз и навсегда для всех типов, состоит в том, что 'Json.format' - это _macro_, который смотрит на определенный тип_ и генерирует для него некоторый код_type-specific_. Очевидно, это не может быть сделано для параметра типа. –

Смежные вопросы