将Scala类型传递给函数

时间:2015-05-17 05:01:26

标签: scala

我试图实施basically the same thing that is discussed here,但在我的具体情况下,它无效。

目前我有一个验证来自我们服务器的JSON响应的函数。问题是,它将JSON类型硬编码到方法中:

    def fakeRequest[A: Writes](target: () => Call, requestObject: A): Any = {
        route(FakeRequest(target()).withJsonBody(Json.toJson(requestObject))) match {
            // ... stuff happens
            Json.parse(contentAsString(response)).validate[GPInviteResponse]
                                                           ^

请注意硬编码的 GPInviteResponse 类型。

因此,为了使它成为一个完全通用且可重用的方法,传入正在验证的类型会很棒。

我试过了:

    def fakeRequest[A: Writes, B](target: () => Call, requestObject: A, responseType: B): Any = {
        route(FakeRequest(target()).withJsonBody(Json.toJson(requestObject))) match {
            // ... stuff happens
            Json.parse(contentAsString(response)).validate[B]
                                                           ^

这几乎可行,但我得到No Json deserializer found for type B.有道理,所以我改为:

    def fakeRequest[A: Writes, B: Reads](target: () => Call, requestObject: A, responseType: B): Any = {

但是,现在我得到No Json deserializer found for type controllers.GPInviteResponse.type.所以问题是:是否可以传递这样的类型(或者还有其他魔法可以使它工作)?

为该类型定义了一个反序列化程序...我已经重读了六次,以确保没有拼写错误:

case class GPInviteResponse(inviteSent: Boolean, URL: Option[String], error: Option[GPRequestError] = None) {
    def this(error: GPRequestError) = this(false, None, Option(error))
}

object GPInviteResponse {
    implicit val readsInviteResponse = Json.reads[GPInviteResponse]
    implicit val writesInviteResponse = Json.writes[GPInviteResponse]
}

修改

下面是一个简化的测试用例,用于演示此问题。目前,这不会编译(错误如下所示)。我想我理解为什么它不能工作(模糊地),但我不知道解决方案。关于它为什么不能工作的理论:虽然提供的类型GPInviteRequest确实有一个隐式的Reads / Writes方法,但Scala无法建立实例B实际上是{{1}的连接所以它得出的结论是GPInviteRequest没有读/写。

B

上述测试结果如下:

  

[错误]   /Users/zbeckman/Projects/Glimpulse/Server-2/project/glimpulse-server/test/application/TestInviteServices.scala:46:   没有找到类型B的Json序列化程序。尝试实现隐式   此类型的写入或格式。 [错误] val json =   Json.toJson(i).toString [错误] ^ [错误]   /Users/zbeckman/Projects/Glimpulse/Server-2/project/glimpulse-server/test/application/TestInviteServices.scala:133:   找不到类型controllers.GPInviteResponse.type的Json反序列化器。   尝试为此类型实现隐式读取或格式。 [错误]             fakeRequest(controllers.routes.GPInviteService.invite,i,   GPInviteResponse)匹配{[error] ^

2 个答案:

答案 0 :(得分:2)

您的错误消息:

  

找不到类型B的Json序列化程序。尝试为此类型实现隐式写入或格式。

在此函数中,toJson方法如何知道如何序列化类型B?

   def tryToValidate[B: Reads, Writes](i: B) = {
        val json = Json.toJson(i).toString
        Json.parse(json).validate[B].isSuccess must beTrue
    }

你没有将编写器/读取器引入范围,编译器不知道在哪里查找,这就是它告诉你实现它的原因。这是一个快速的解决方案

case class GPInviteResponse(inviteSent: Boolean)
object GPInviteResponse {
  implicit val format = Json.format[GPInviteResponse]
}

def tryToValidate[B](i: B)(implicit format: Format[B]) = {
  val json = Json.toJson(i).toString
  Json.parse(json).validate[B]
}

注意:使用format方法相当于定义readswrites

现在,范围内有B的隐式格式化程序,因此编译器知道在哪里找到它并将其注入validate方法,该方法隐含地采用reader

// From play.api.libs.json

def validate[T](implicit rds: Reads[T]): JsResult[T] = rds.reads(this)

修改

您可以向函数添加类型参数,然后在validate[T]方法中引用它们,如下所示:

// Define another case class to use in the example
case class Foo(bar: String)
object Foo {
  implicit val format = Json.format[Foo]
}

def tryToValidate[B, C](implicit f1: Format[B], f2: Format[C]) = {

  val j1 = """{"inviteSent":true}"""
  val j2 = """{"bar":"foobar"}""" // 

  Json.parse(j1).validate[B]
  Json.parse(j2).validate[C]
}

// Example call
tryToValidate[GPInviteResponse, Foo]

答案 1 :(得分:1)

以这种方式尝试:

def tryToValidate[B](i: B)(implicit format : Format[B]) = {
        val json = Json.toJson(i).toString
        Json.parse(json).validate[B].isSuccess must beTrue
    }