0

我有一些简单的消息,它们隐含Json.readsJson.formats在它们的伴随对象中定义。所有这些消息都扩展了MyBaseMessage.

换句话说,对于任何T <: MyBaseMessage,T都是(反)可序列化的。

这些消息表示要在集群上执行的简单 CRUD 操作,因此在发送 JSON 的 CLI 和集群之间有一个 Play 服务器。因为操作简单,所以我应该可以Action在 Play 端做一些非常通用的 s:当我在一个端点接收到 JSON 时,根据端点反序列化消息并将该消息转发到集群。

我的最终目标是做这样的事情:

// AddBooMessage extends MyBaseMessage
def addBoo = FooAction[AddBooMessage]  

// AddMooMessage extends MyBaseMessage
def addMoo = FooAction[AddMooMessage]

// etc. ...

所以当请求发送到addBoo消息对应的路由时,请求的JSON会被解析成AddBooMessage消息并推送到集群中。重复恶心。

我写了以下内容:

  private def FooAction[T <: MyBaseMessage] = Action {
    implicit request =>
       parseAndForward[T](request)
  } 

  private def parseAndForward[T <: MyBaseMessage](request: Request[AnyContent]) = {
    val parsedRequest = Json.parse(request.body.toString).as[T]
    Logger.info(s"Got '$parsedRequest' request. Forwarding it to the Cluster.")
    sendToCluster(parsedRequest)
  }

但我发现以下错误:

没有为 type 找到 Json 反序列化器T。尝试为这种类型实现一个隐式ReadsFormat

但是,所有这些消息都是可序列化的,并且两者都有ReadsFormat为它们定义。

我试图传递(implicit fjs: Reads[T])parseAndForward希望隐式提供Reads所需的(尽管它应该已经隐式提供),但它没有帮助。

我怎么解决这个问题?

4

1 回答 1

2

JsValue#as[A]需要一个隐式Reads[A]才能将 JSON 反序列化为某种类型A。也就是说,您收到的错误消息是因为编译器无法保证存在Reads[T]for any type T <: MyBaseMessage。假设sendToCluster以相同的方式参数化,这可以通过简单地Reads[T]在每个方法调用中要求隐式来轻松修复。Reads[T]听起来您很亲密,只需要通过要求from来使事情更进一步FooAction(因为该调用是确定类型的地方)。

private def FooAction[T <: MyBaseMessage : Reads] = Action { implicit request =>
  parseAndForward[T](request)
} 

private def parseAndForward[T <: MyBaseMessage : Reads](request: Request[AnyContent]) = {
  val parsedRequest = Json.parse(request.body.toString).as[T]
  Logger.info(s"Got '$parsedRequest' request. Forwarding it to the Cluster.")
  sendToCluster(parsedRequest) // Assuming this returns a `Future[Result]`
}

如果您打算通过手动提供类型参数来使用上述代码,那么这将很好。


我认为您可以在这里进行一些其他改进。首先,如果你总是期望 JSON,你应该需要parse.json BodyParser. BadRequest如果收到的甚至不是 JSON,这将返回一个。其次,as如果接收到的 JSON 无法反序列化为预期的类型,则会抛出异常,您可以使用它JsValue#validate来更安全地执行此操作,fold并将结果显式处理成功和错误情况。例如:

private def FooAction[T <: MyBaseMessage] = Action.async(parse.json) { implicit request =>
  parseAndForward[T](request)
} 

private def parseAndForward[T <: MyBaseMessage](request: Request[JsValue]) = {
  request.body.validate[T].fold(
    error => {
      Logger.error(s"Error parsing request: $request")
      Future.successful(BadRequest)
    },
    parsed => {
      Logger.info(s"Got '$parsed' request. Forwarding it to the Cluster.")
      sendToCluster(parsed)
    }
  )
}
于 2017-02-27T17:41:03.413 回答