5

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.

2 Answers 2

4

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

Sign up to request clarification or add additional context in comments.

4 Comments

Is this different from the Format instance which has read and write methods?
I think format is a 2.1 feature? I'm not sure, but in anycase, you only need reads for deserializing.
I have a format with both read and write methods. The code compiles, I think there is a problem with the way I am mapping the json request in my controller.
Format is not so different. It just involve reads and writes. I didn't make it running too. Please let me know if you figure out.
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.