【问题标题】:Passing a Scala type to a function将 Scala 类型传递给函数
【发布时间】:2015-05-17 05:01:25
【问题描述】:

我正在尝试实现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 确实具有隐式读/写方法,但 Scala 无法建立实例 B 实际上是 GPInviteRequest 的连接,因此它得出结论 @987654331 @ 没有读/写。

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 序列化程序。尝试实现隐式 写入或格式化此类型。 [错误] 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, 我, GPInviteResponse) 匹配 { [错误] ^

【问题讨论】:

  • 你能举一个最小的例子来编译和展示问题吗?

标签: scala


【解决方案1】:

您的错误信息:

找不到类型 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 在作用域中有一个隐式格式化程序,因此编译器知道在哪里可以找到它并将其注入到隐式采用 readervalidate 方法中:

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

【讨论】:

  • 我没有意识到 format() 基本上是一起读/写的,谢谢。这是一个很好的答案......但是,我无法将它应用于“现实世界”案例,这稍微复杂一些。此函数签名:def fakeRequest[A, B](target: () => Call, request: A, response: B)(implicit f1: Format[A], f2: Format[B]) 给出与以前相同的错误(“未找到 GPInviteResponse.type 的 Json 格式化程序”)。似乎添加第二种类型(分离请求和响应类型)使其复杂化。
  • 啊,我现在看到了问题:在示例中,传入了一个实例。在我的“真实世界”案例中,我指定了一个 type(不是实例)。有没有办法传递实际的 type(例如 GPInviteRequest)而不是实例?
【解决方案2】:

试试这个方法:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-23
    • 2011-12-01
    • 2019-06-19
    • 2015-07-18
    • 2015-09-11
    • 1970-01-01
    • 1970-01-01
    • 2017-04-15
    相关资源
    最近更新 更多