2015-05-17 148 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 
    }