如何通过 Scala 中的参数化类型获取隐式 val/def?

How to obtain implicit val/def via parameterized types in scala?

我正在使用 Playframework 2.5 并尝试制作一个所有 Json request/response 控制器方法调用的方法,以避免在每个控制器方法内部编写 implicit val foo = Json.writes[Bar] or reads[Bar] 以及处理基本验证和 return 错误代码。

将由每个控制器继承的BaseController

class BaseController @Inject()(implicit exec: ExecutionContext) extends Controller {
  def handleJson[T <: RequestModel,U <: ResponseModel](request: Request[JsValue])(cb: (T) => Future[U]):Future[Result] = {
    implicit val convertReq = Json.reads[T]
    implicit val convertRes = Json.writes[U]
    val reqOpt = request.body.asOpt[T]
    reqOpt match {
      case Some(data) =>
        cb(data).map{x => Ok(Json.toJson(x))}
      case None =>
        Future.successful(Ok(Json.obj("foo" -> "bar")))
    }
  }
}

控制器

  def send = Action.async(parse.json) { req =>
    handleJson[RequestCaseClass,ResponseCaseClass](req)( (x)  =>
      injectedClass.foo(x.bar).map{ case (success: Boolean,mes: String) =>
        if(success) SendRes(SomethingForSuccess)
        else SendRes(SomethingForError)
      }
    )
  }

上面的代码无法编译,因为 handleJsonimplicit val convertReq = Json.reads[T]implicit val convertRes = Json.writes[U] 表示

No Json deserializer found for type T. Try to implement an implicit Reads or Format for this type.

我已经尝试制作 case class 的伴随对象并将隐式放入其中,尽管这不能解决任何问题。

我的问题是,如何使用参数化类型的隐式值?

提前致谢。

您可以在调用 handleJson 之前初始化 implicit 变量,并将 convertReqconvertRes 设置为 handleJson:

的隐式方法参数
def send = Action.async(parse.json) { req =>
    implicit val convertReq = Json.reads[RequestCaseClass]
    implicit val convertRes = Json.writes[ResponseCaseClass]
    handleJson[RequestCaseClass,ResponseCaseClass](req)( (x)  =>
      injectedClass.foo(x.bar).map{ case (success: Boolean,mes: String) =>
        if(success) SendRes(SomethingForSuccess)
        else SendRes(SomethingForError)
      }
    )
}

def handleJson[T <: RequestModel,U <: ResponseModel](request: Request[JsValue])(cb: (T) => Future[U])(implicit convertReq: Reads, convertRes: Writes):Future[Result] = {
    val reqOpt = request.body.asOpt[T]
    reqOpt match {
      case Some(data) =>
        cb(data).map{x => Ok(Json.toJson(x))}
      case None =>
        Future(Ok(Json.obj("foo" -> "bar")))
    }
  }

我建议这样做

class Application @Inject()(implicit exec: ExecutionContext) extends Controller {
  def handleJson[A : Reads, B : Writes](request: Request[JsValue])(cb: A => Future[B]): Future[Result] = {
    val reqOpt = request.body.asOpt[A]
    reqOpt match {
      case Some(data) =>
        cb(data).map { x => Ok(Json.toJson(x)) }
      case None =>
        Future.successful(Ok(Json.obj("foo" -> "bar")))
    }
  }

  def index = Action.async(parse.json) { req =>
    handleJson[MyReq, MyResp](req)(x => Future.successful(MyResp((x.i to x.i + 4).toList)))
  }
}

您不必用任何特征来约束您的模型,只要隐含的 ReadsWrites 存在即可。

为确保这一点,最好在 class 的伴随对象中定义 ReadsWrites。当您导入模型时,您会将它们带到隐式范围 classes.

object MyReq {
  implicit val myReqReads: Reads[MyReq] = Json.reads[MyReq]
}
case class MyReq(i: Int)

object MyResp {
  implicit val myRespWrites: Writes[MyResp] = Json.writes[MyResp]
}
case class MyResp(l: List[Int])