jislam
jislam

Reputation: 842

What is the wrong while trying to parse Future response?

I have a function which will take a Token via ajax request. It will return a response a list of directories while the token is valid.

def all = Action.async(parse.json) {
  implicit request => tokenForm.bind(request.body).fold(
    formWithErrors => Future.successful(BadRequest(formWithErrors.toString)),
    form => checkToken(form.token).map(token => {
      val directories = Directories.all.map(directory => {
        Json.toJson(directory)
      })
      Ok(Json.obj("status" -> {if (token.get.id.getOrElse(0) >= 1) true else false}, "message" -> {if (token.get.id.getOrElse(0) >= 1) Json.toJson(directories) else "Invalid token"}))
    })
  )
}

While I run the above code It says [As ^ sign indicate the position the error found]

No Json serializer found for type scala.concurrent.Future[play.api.libs.json.JsValue]. Try to implement an implicit Writes or Format for this type.
Ok(Json.obj("status" -> {if (token.get.id.getOrElse(0) >= 1) true else false}, "message" -> {if (token.get.id.getOrElse(0) >= 1) Json.toJson(directories) else "Invalid token"}))
                                                                                                                                            ^

Upvotes: 3

Views: 206

Answers (1)

Łukasz
Łukasz

Reputation: 8673

Here is what I come up with

def all = Action.async(parse.json) {
  implicit request => tokenForm.bind(request.body).fold(
    formWithErrors => Future.successful(BadRequest(formWithErrors.toString)),
    form => for {
      (token, directories) <- checkToken(form.token) zip Directories.all
    } yield {
      val isTokenValid = token.isDefined
      val responseBody = Json.obj(
        "status" -> isTokenValid, 
        "message" -> {
          if (isTokenValid)
            Json.toJson(directories)
          else
            "Invalid token"
        }
      )
      Ok(responseBody)
    }
  )
}

asuming checkToken and Directories.all returns Future.

Your function needs to return Future[Result]. When you are folding, the first branch is correct

formWithErrors => Future.successful(BadRequest(formWithErrors.toString))

However in the second branch it seems like you started a new Future by calling Directories.all, and you want to serialize it and return as json. There is no serializer for Future[JsValue] as this makes no sense, it would have to block to get result. You need to somehow get to the values of both checkToken(form.token) and Directories.all.

You can do this as I did above, or for example like this:

form => {
  val futureToken = checkToken(form.token)
  val futureDirectories = Directories.all

  for {
    token <- futureToken
    directories <- futureDirectories
  } yield {
     // same code as above
  }
}

Notice that if you would inline futureToken and futureDirectories they would be executed serially.

Also note that you are converting your directory list to json twice.

Once here

val directories = Directories.all.map(directory => {
  Json.toJson(directory)
})

Asuming Directories.all returns Future[List[Directory]], then when you use map, the function you pass operates on List[Directory] so the variable should be named directories not directory. It will work, play easly converts list of anything convertible to json, no need to do it manually.

Second time you did it here

Json.toJson(directories)

Upvotes: 5

Related Questions