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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With