Как решить эту ошибку компиляции Scala/Play (возвращается неправильный тип)?
Я пытаюсь написать десериализатор JSON, который подключается к контроллерам Play Framework вместо стандартной библиотеки Play JSON. Обоснование заключается в том, чтобы иметь возможность использовать Джексона напрямую. Мне удалось создать подключаемый десериализатор, благодаря рецепту Маартена Винкельса, но я застрял из-за ошибки компиляции, которую я просто не понимаю (отказ от ответственности: я новичок в 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)
}
}
}
}
С другой стороны,
Если вы, ребята, знаете, как лучше подключить пользовательский десериализатор JSON к Play, поверх Джексона, это тоже будет приемлемо. Это то, что я пытаюсь сделать здесь в конце концов.
2 ответа
eofOrElse
Iteratee
оборачивает результат предыдущего Iteratee
в Either
, Потому что результат предыдущего Iteratee был уже Either
, вы в конечном итоге с чем-то вроде Either[Result, Either[Result, A]]
, Вызов joinRight
может превратить это в Either[Result, A]
мы требуем. Также _.global.onBadRequest(request, "Invalid Json")
возвращает Future[SimpleResult]
не SimpleResult
- Я удалил этот код.
Ниже я применил эти исправления, а также упростил кортеж, возвращенный из .left.map
звоните, а также использовали transform
вместо apply
чтобы покончить с последним 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)
}
}
Эта строка:
case Left(b) => Done(Left(b), Empty)
Звонит Done
с Left(b)
где b
имеет тип play.api.mvc.Results.Status
, Это устанавливает ожидание для типа итерируемого, который будет возвращен вмещающим flatMap
,
На этой линии:
case Left((r, in)) => Done(Left(r), El(in))
Done
вызывается с Left(r)
где r
имеет тип Object
в результате итератор другого типа, чем case Left
ветка возвращается.
Изменение этой строки на:
case Left((r: Result, in)) => Done(Left(r), El(in))
производит итератор того же типа, что и предыдущий, избегая ошибки компиляции.
Не углубляясь в алгоритм, нельзя сказать, является ли это подходящим изменением, но более общий ответ заключается в том, что все ветви кода должны возвращать совместимые типы.
В качестве подсказки, используя плагин Scala для Eclipse, вы можете навести указатель мыши на переменную, чтобы увидеть ее тип. Иногда разделение кода на куски, назначенные явно типизированным переменным, также может прояснить, с какими типами связаны ошибки, за счет того, что код становится более многословным.