Handling JSON requests in Play Framework 2.0 Scala - json

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.

Related

PlayJSON in Scala

I am trying to familiarize myself with the PlayJSON library. I have a JSON formatted file like this:
{
"Person": [
{
"name": "Jonathon",
"age": 24,
"job": "Accountant"
}
]
}
However, I'm having difficulty with parsing it properly due to the file having different types (name is a String but age is an Int). I could technically make it so the age is a String and call .toInt on it later but for my purposes, it is by default an integer.
I know how to parse some of it:
import play.api.libs.json.{JsValue, Json}
val parsed: JsValue = Json.parse(jsonFile) //assuming jsonFile is that entire JSON String as shown above
val person: List[Map[String, String]] = (parsed \ "Person").as[List[Map[String, String]]]
Creating that person value throws an error. I know Scala is a strongly-typed language but I'm sure there is something I am missing here. I feel like this is an obvious fix too but I'm not quite sure.
The error produced is:
JsResultException(errors:List(((0)/age,List(JsonValidationError(List(error.expected.jsstring),WrappedArray())))
The error you are having, as explained in the error you are getting, is in casting to the map of string to string. The data you provided does not align with it, because the age is a string. If you want to keep in with this approach, you need to parse it into a type that will handle both strings and ints. For example:
(parsed \ "Person").validate[List[Map[String, Any]]]
Having said that, as #Luis wrote in a comment, you can just use case class to parse it. Lets declare 2 case classes:
case class JsonParsingExample(Person: Seq[Person])
case class Person(name: String, age: Int, job: String)
Now we will create a formatter for each of them on their corresponding companion object:
object Person {
implicit val format: OFormat[Person] = Json.format[Person]
}
object JsonParsingExample {
implicit val format: OFormat[JsonParsingExample] = Json.format[JsonParsingExample]
}
Now we can just do:
Json.parse(jsonFile).validate[JsonParsingExample]
Code run at Scastie.

Scala Play Json implicit writes type mismatch

I am new to Scala and Play, and I ask for help with this simple example. I tried to search for solution by myself, but I did not succeed.
I am trying to do the example from from Mastering Play Framework for Scala book, the one about extending Json parser (Pages 29-30).
The environment I use is:
Scala: 2.11.7
Play: 2.5.8
Activator: 1.3.10
The code is:
case class Subscription(emailId: String, interval: Long)
In controller:
import play.api.libs.json.Json
import play.api.libs.json.JsValue
import play.api.libs.json.Writes
.....
val parseAsSubscription = parse.using {
request =>
parse.json.map {
body =>
val emailId:String = (body \ "emailId").as[String]
val fromDate:Long = (body \ "fromDate").as[Long]
Subscription(emailId, fromDate)
}
}
implicit val subWrites:Writes[Subscription] = Json.writes[Subscription]
def getSub = Action(parseAsSubscription) {
request =>
val subscription: Subscription = request.body
Ok(Json.toJson(Subscription))
}
The line: Ok(Json.toJson(Subscription)) gives an error
No Json serializer found for type models.Subscription.type. Try to
implement an implicit Writes or Format for this type.
This is odd, because Writes object is defined one row above. Thus, I tried to pass it to toJson method explicitly:
Ok(Json.toJson(Subscription)(subWrites))
It gave me a different error, which partially explained why existing Writes object did not suit:
type mismatch;
found:
play.api.libs.json.Writes[models.Subscription]
required:
play.api.libs.json.Writes[models.Subscription.type]
However, I don't understand the nature of this error and what models.Subscription.type is .
I used to do a similar thing in a different example, and it worked just fine.
Any help will be appreciated.
You're trying to serialize the type Subscription, rather than the request body, which you stored as the value subscription. Try replacing the last line with Ok(Json.toJson(subscription)).

How to send json string body for FakeRequest without using withJsonBody in Play framework v2.x?

I have play scala v2.3 application. I'm trying to create a controller test by sending json string with FakeRequest as below:
class ApplicationSpec extends Specification {
"Application" should {
"Create Bob Test" in new WithApplication {
val jsonStr = """{"text": "hi bob"}"""
val result = route(FakeRequest(POST, "/bob")
.withHeaders("Content-Type" -> "application/json")
.withBody(jsonStr)
).get
status(result) === OK
}
}
}
The controller:
object Application extends Controller {
def bob = Action.async { request =>
println("request.headers: " + request.headers)
println("request.body: " + request.body)
println("request.body.asJson: " + request.body.asJson)
request.body.asJson.map { json =>
// do something with the json
Future.successful(Ok)
}.getOrElse(Future.successful(BadRequest))
}
}
When I run the test, it's failed and this is what printed:
request.headers: ArrayBuffer((Content-Type,List(text/plain; charset=utf-8)))
request.body: AnyContentAsText({"text": "hi bob"})
request.body.asJson: None
So the Content-Type header sent is not application/json despite I have set the header with application/json. And probably because of that, the request.body.asJson returns None.
Anyone know how to solve this?
Note: I know I can use .withJsonBody(Json.parse(jsonStr)) on the FakeRequest and it will succeed, but with this way I can't send broken or invalid json string for negative test case since withJsonBody accepts JsValue which the json string must be converted first with Json.parse.
By default, the Content-Type header gets overridden by the framework. A workaround is given in the blog
In your case, this should work
route(FakeRequest(POST, "/bob", FakeHeaders(Seq(CONTENT_TYPE->Seq("application/json"))), jsonStr))(Writeable(_.getBytes, None)).get
For multiple tests, an implicit can be created in the beginning for the writable and it need not to be passed in every test:
implicit val wString: Writeable[String] = Writeable(_.getBytes, None)

Get a String from a JSon request

i'm writing a Play 2.3.2 application in Scala.
I'm writing a Statistic controller that queries my mongodb database for obtain some informations.
Now i'm trying to implement a method that returns all the tags associate to a user.
I get an http request in json format as the following:
{
"user": "example#example.com"
}
I want to parse the Json request and get the String associate to the "user" field of the Json, if the Json is correctly i want to do some work with the String object, otherwise i want to return a BadRequest response.
My method implementation is something like this:
def userEmail = Action.async { request =>
val userEmail: String = request.body.asJson.map { json =>
json.validate[String].map {
//if the json congaing the "user tag" return the instance, get the instance, otherwise return a BadRequestInstance
}
}
def elaborate(user: String)= {
//some work
}
elaborate(userEmail)
}
How can i make that??
As johanandren mentioned reading playframework documentation should solve your problem.
Hint: I would define case class and implicit reads to convert json data into case class type.
case class User(email: String)
implicit val userReads = (
(JsPath \ "email").read[String]
)(User.apply _)

How to respond with a pretty-printed JSON object using play framework?

How can one send, using the Play! framework, a JSON response that is formatted to be human-readable?
For example, I'm looking for something like:
def handleGET(path:String) = Action{ implicit request =>
val json = doSomethingThatReturnsAJson(path,request)
request.getQueryString("pretty") match {
case Some(_) => //some magic that will beautify the response
case None => Ok(json)
}
}
My search led me to JSON pretty-print, which was not very helpful on it's own, but it did say the ability should be integrated in future versions. That was play 2.1.X, so, I guess it already exists somewhere in the 2.2X version of play?
Play framework has pretty printing support built-in:
import play.api.libs.json.Json
Json.prettyPrint(aJsValue)
So in your case, it would be sufficient to do the following:
def handleGET(path:String) = Action { implicit request =>
val json = doSomethingThatReturnsAJson(path, request)
request.getQueryString("pretty") match {
case Some(_) => Ok(Json.prettyPrint(json)).as(ContentTypes.JSON)
case None => Ok(json)
}
}
You can use Gson to pretty print Json string, don't know about scala; but here is a Java example which you can convert to scala and use it:
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String jsonStr = gson.toJson(obj);
System.out.println(jsonStr);