2015-05-17 109 views
2

我试图实现basically the same thing that is discussed here,但在我的特定情况下,它不起作用。将Scala类型传递给函数

目前我有一个函数验证来自服务器的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确实有隐式读/写方法,但Scala无法建立实例B实际上是GPInviteRequest的连接,因此它得出结论认为B没有读/写。

case class GPInviteResponse(inviteSent: Boolean) 
object GPInviteResponse { 
    implicit val readsInviteResponse = Json.reads[GPInviteResponse] 
    implicit val writesInviteResponse = Json.writes[GPInviteResponse] 
} 
class TestInviteServices extends PlaySpecification { 
    "try to validate a type" in { 
     tryToValidate(GPInviteRequest(true)) 
    } 
    def tryToValidate[B: Reads, Writes](i: B) = { 
     val json = Json.toJson(i).toString 
     Json.parse(json).validate[B].isSuccess must beTrue 
    } 
} 

上述试验得到:

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

+0

你能给出一个编译和展示问题的简单例子吗? –

回答

2

你的错误消息:

没有JSON序列中发现了B型试为此类型实现隐式写入或格式。

在这个函数中,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] 
+0

我没有意识到格式()基本上是读/写在一起,谢谢。这是一个很好的答案......但是,我很难将它应用到稍微复杂一点的“现实世界”案例中。这个函数签名:'def fakeRequest [A,B](target:()=> Call,request:A,response:B)(implicit f1:Format [A],f2:Format [B])'像以前一样错误(“找不到用于GPInviteResponse.type的Json格式化程序”)。看来增加第二种类型(分离请求和响应类型)使它变得复杂。 – Zac

+0

啊,我现在看到了问题:在这个例子中,一个实例被传入。在我的“真实世界”情况下,我指定了一个**类型**(不是实例)。有没有办法传递实际的**类型**(例如'GPInviteRequest')而不是实例? – Zac

+0

编辑我的答案 –

1

试试这样说:

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