1

我正在尝试编写一个插入 Play Framework 控制器的 JSON 反序列化器,以代替标准的 Play JSON 库。理由是能够直接使用杰克逊。多亏了 Maarten Winkels 的食谱,我已经能够想出一个可插入的反序列化器,但是由于我不理解的编译错误而被卡住了(免责声明:我是 Scala 新手)。

编译错误源于这样一个事实,显然一个分支JsonObjectParser.apply试图返回一个实例Object,而它应该是Result。我不明白为什么会这样。我的问题是,我该如何解决这个错误?

编译错误

编译错误如下所示:

/Users/arve/Projects/test/JsonObjectParser.scala:26: type mismatch;
[error]  found   : Object
[error]  required: play.api.mvc.Result
[error]         case Left((r, in)) => Done(Left(r), El(in))

JsonObjectParser.scala

这是有问题的源代码:

import java.io.{ByteArrayInputStream, InputStream}

import play.api.Play
import play.api.libs.iteratee.Input._
import play.api.libs.iteratee._
import play.api.mvc._
import scala.concurrent.ExecutionContext.Implicits.global

class JsonObjectParser[A: Manifest](deserializer: (InputStream) => A) extends BodyParser[A] {
  val JsonMaxLength = 4096

  def apply(request: RequestHeader): Iteratee[Array[Byte], Either[Result, A]] = {
    Traversable.takeUpTo[Array[Byte]](JsonMaxLength).apply(Iteratee.consume[Array[Byte]]().map { bytes =>
      scala.util.control.Exception.allCatch[A].either {
        deserializer(new ByteArrayInputStream(bytes))
      }.left.map { e =>
        (Play.maybeApplication.map(_.global.onBadRequest(request, "Invalid Json")).getOrElse(
          Results.BadRequest), bytes)
      }
    }).flatMap(Iteratee.eofOrElse(Results.EntityTooLarge))
      .flatMap {
      case Left(b) => Done(Left(b), Empty)
      case Right(it) => it.flatMap {
        // Won't compile
        case Left((r, in)) => Done(Left(r), El(in))
        case Right(a) => Done(Right(a), Empty)
      }
    }
  }
}

或者,

如果你们知道在 Jackson 之上将自定义 JSON 反序列化器插入 Play 的更好方法,那也是可以接受的。毕竟,这就是我要在这里做的事情。

4

2 回答 2

2

eofOrElse Iteratee前一个的结果包装Iteratee成一个Either. 因为前一个 Iteratee 的结果已经是Either,所以你最终会得到类似Either[Result, Either[Result, A]]. 调用joinRight可以将其转换为Either[Result, A]我们需要的。还_.global.onBadRequest(request, "Invalid Json")返回 a Future[SimpleResult],而不是 a SimpleResult- 我已删除该代码。

下面我已经应用了这些修复程序,并简化了从.left.mapcall 返回的元组,并且也使用transform而不是apply为了取消 last flatMap

class JsonObjectParser[A: Manifest](deserializer: (InputStream) => A) extends BodyParser[A] {
  val JsonMaxLength = 4096

  def apply(request: RequestHeader): Iteratee[Array[Byte], Either[SimpleResult, A]] = {
    Traversable.takeUpTo[Array[Byte]](JsonMaxLength).transform {
      Iteratee.consume[Array[Byte]]().map { bytes =>
        scala.util.control.Exception.allCatch[A].either {
          deserializer(new ByteArrayInputStream(bytes))
        }.left.map { _ =>
          Results.BadRequest
        }
      }
    }.flatMap(Iteratee.eofOrElse(Results.EntityTooLarge)).map(_.joinRight)
  }
}
于 2014-07-10T16:21:42.910 回答
0

这一行:

case Left(b) => Done(Left(b), Empty)

Done正在调用Left(b)where bis 类型play.api.mvc.Results.Status。这设置了将由封闭返回的迭代对象的类型的期望flatMap

在这条线上:

case Left((r, in)) => Done(Left(r), El(in))

DoneLeft(r)使用where ris of type调用Object,导致迭代的类型与case Left分支返回的类型不同。

将该行更改为:

case Left((r: Result, in)) => Done(Left(r), El(in))

生成与前一个相同类型的迭代器,避免编译错误。

如果不深入研究算法,就无法判断这是否是适当的更改,但更一般的答案是所有代码分支都必须返回兼容的类型。

作为提示,使用 Eclipse 的 Scala 插件,您可以将鼠标悬停在变量上以查看其类型。有时,将代码分成分配给显式类型变量的块也可以更清楚地处理哪些类型,但代价是使代码更冗长。

于 2014-07-10T15:49:13.847 回答