Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling JSON requests in Play Framework 2.0 Scala

I am trying to send data from the client to the server using a JSON request. The body of the JSON request looks like this:

{
"upload":
    {
    "ok":"some message",
    "assemblyId":"a9d8f72q3hrq982hf98q3"
    }
}

Play is able to recognize the request body as JSON but when I try to parse individual values, namely the "upload" object, Play complains that it can't find the specified parameter.

The Scala method is as follows:

def add(course:Long) = withAccount { account => implicit request =>
  println()
  println(request.body)  // output: AnyContentAsJson({"upload":{"ok":"ASSEMBLY_COMP...

  request.body.asJson.map { json =>
    println()
    println(json)  // output: {"upload":{"ok":"ASSEMBLY_COMPLETED","assemb...

    (json \ "upload").asOpt[models.SomeClass].map { upload =>
      Ok("Got upload")
    }.getOrElse {
      BadRequest("Missing parameter [upload]")
    }
  }.getOrElse {
    BadRequest("Expecting Json data")
  }
}

I'm having trouble understanding why the above code fails. The method has no trouble mapping the request body to a json object. The "println(json)" command prints out the exact same thing that Chrome shows me as the 'Request Payload'. Yet, when I try to grab the root object, "upload", it fails. And the method returns a bad request complaining about the missing parameter.

like image 425
Dave Lee Avatar asked Nov 17 '12 01:11

Dave Lee


2 Answers

To do asOpt[models.SomeClass] there needs to be a Reads instance for it to work.

Here is an example

case class SomeClass(ok: String, assemblyId: String)
implicit object SomeClassReads extends Reads[SomeClass] {
  def reads(json: JsValue) = 
    SomeClass((json \ "ok").as[String], (json \ "assemblyId").as[String])
}

You can see how you would implement a Reads instance at https://github.com/playframework/Play20/blob/2.0.x/framework/src/play/src/main/scala/play/api/libs/json/Reads.scala#L35

like image 161
Ivan Meredith Avatar answered Sep 29 '22 07:09

Ivan Meredith


If you use play 2.1x, Reads has changed a bit from 2.0x and it's probably your main problem(like me).

You can find a very good explanation here.

Simply this code works fine:

import play.api.libs.json._
import play.api.libs.functional.syntax._

case class Person(name: String, age: Int, lovesChocolate: Boolean)

implicit val personReads = Json.reads[Person]

It look amazing isn't it? But there are some points that you should pay attention:

  • Implicit definition should be in controller. Of course there are some other ways to do it.

  • If your model is in models class(it's in controller at the example above) you shouldn't name your object same with your class. In that case it doesn't work:

    case class Person(name: String, age: Int, lovesChocolate: Boolean)    
    
    object Person{....}  //this won't work
    

This way have big advantages. I strongly recommend you to check out this blog.

like image 33
kubudi Avatar answered Sep 29 '22 07:09

kubudi