Как решить эту ошибку компиляции 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 ответа

Решение

eofOrElseIteratee оборачивает результат предыдущего 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, вы можете навести указатель мыши на переменную, чтобы увидеть ее тип. Иногда разделение кода на куски, назначенные явно типизированным переменным, также может прояснить, с какими типами связаны ошибки, за счет того, что код становится более многословным.

Другие вопросы по тегам