getOrElse Scala Play JSON Payload - json

I am trying to not only clean this code up, but it is breaking when the request.body isEmpty, which defeats the purpose of this code segment. I have been referring to Play's documentation to implement getOrElse, but I'm having a hard time trying to see how I can turn my code segment into what they've provided. Here is the method:
override def createWOTC(clientID: Int) =
Action(parse.json) { implicit request =>
val partnerID = {
if ((request.body \ "partner_id").as[String].isEmpty) "jobapp"
else (request.body \ "partner_id").as[String]
}
val partnerAuth = {
if ((request.body \ "partner_auth").as[String].isEmpty) "(snip)"
else (request.body \ "partner_auth").as[String]
}
val handshakeURL = {
if ((request.body \ "handshake_url").as[String].isEmpty) "(snip)"
else (request.body \ "handshake_url").as[String]
}
...
wotcService.createWOTC(clientID, (request.body \ "company").as[String], (request.body \ "auth").as[String],
partnerID, partnerAuth, handshakeURL, eligibilityURL, hireURL, pdfURL)
NoContent
}

val partnerAuth = (request.body \ "partner_auth").asOpt[String].getOrElse("SxtWJbboccljiaii")

Related

How could I properly work with Scala Play Read?

According to this documentation (official):
https://www.playframework.com/documentation/2.8.x/ScalaJsonCombinators
I have to create a case class, after that I have to create a JsonReader:
val nameReads: Reads[String] = (JsPath \ "name").read[String]
then
val nameResult: JsResult[String] = json.validate[String](nameReads)
So, the result would be into nameResult and it was expecting that data was accessible like this:
println(nameResult.name)
Unfortunately, it doesn't work. It doesn't print results or return them.
First of all I work with Future and read JSON from web
implicit val context = scala.concurrent.ExecutionContext.Implicits.global
val userReads: Reads[User] = (
(JsPath \ "id").read[Int] and
(JsPath \ "login").read[String]
)
val futureResult = wc.url(path).get().map {
response =>
response.json.validate[User](userReads)
}
futureResult.map(r => println(r.id, r.login))
But! This code works, but it isn't in documentation.
implicit val context = scala.concurrent.ExecutionContext.Implicits.global
val userReads: Reads[User] = (
(JsPath \ "id").read[Int] and
(JsPath \ "login").read[String]
)
val futureResult = wc.url(path).get().map {
response =>
UserTest(
(response.json \ "id").as[String],
(response.json \ "login").as[String]
)
}
futureResult.map(r => println(r.id, r.login))
Does somebody know why code into documentation doesn't work? What is wrong with it?
Could I use my code?
Calling validate[User] doesn't return a User but a JsResult[User]. This is because the JSON data might be invalid and your code needs to handle this case. There is an example in the documentation that you have linked to:
json.validate[Place] match {
case JsSuccess(place, _) => {
val _: Place = place
// do something with place
}
case e: JsError => {
// error handling flow
}
}

Scala Graph JSON with Play Framework

I am trying to pass in a POST request to a REST API developped with Play! (2.5) an object that I would like to use a Scala Graph (from the graph-core dependency).
It looks like the graph already has JSON serialization/deserialization methods based on lift-json, but I am not sure how to "plug" that into Play Json library. Until now I was using implicit converters (with Reads/Writes methods) but I would like to avoid having to write my own methods for the graph part since it is already part of the library itself.
For instance, let's say I have this code:
import java.util.UUID
import scalax.collection.Graph
case class Task(
id: UUID,
status: String)
case class Stuff(
id: UUID = UUID.randomUUID(),
name: String,
tasks: Option[Graph[Task, DiEdge]])
implicit val stuffWrites: Writes[Stuff] = (
(JsPath \ "id").write[UUID] and
(JsPath \ "name").write[String]
)(unlift(Stuff.unapply))
implicit val stuffReads: Reads[Stuff] = (
(JsPath \ "id").read[UUID] and
(JsPath \ "name").read[String]
)(Stuff.apply _)
implicit val taskWrite: Writes[Task] = (
(JsPath \ "id").write[UUID] and
(JsPath \ "status").write[String]
)(unlift(Task.unapply))
implicit val taskReads: Reads[Task] = (
(JsPath \ "id").read[UUID] and
(JsPath \ "status").read[String]
)(Task.apply _)
I miss the part to serialize the graph and the parenting. Should I rewrite everything from scratch, or can I rely on methods toJson/fromJson from scalax.collection.io.json ?
Since I struggled a bit to get this working, I thought I would share the code:
class UUIDSerializer extends Serializer[UUID] {
private val UUIDClass = classOf[UUID]
def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), UUID] = {
case (TypeInfo(UUIDClass, _), json) => json match {
case JString(id) => UUID.fromString(id)
case x => throw new MappingException("Can't convert " + x + " to UUID")
}
}
def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
case x: UUID => JString(x.toString)
}
}
val extraSerializers = new UUIDSerializer :: Nil
implicit val formats = Serialization.formats(NoTypeHints) ++ extraSerializers
val taskDescriptor = new NodeDescriptor[Task](typeId = "Tasks", customSerializers=extraSerializers) {
def id(node: Any) = node match {
case Task(id, _) => id.toString
}
}
val quickJson = new Descriptor[Task](
defaultNodeDescriptor = taskDescriptor,
defaultEdgeDescriptor = Di.descriptor[Task]()
)
implicit val tasksWrites = new Writes[Graph[Task, DiEdge]] {
def writes(graph: Graph[Task, DiEdge]): JsValue = {
val json = graph.toJson(quickJson)
Json.parse(json.toString)
}
}
implicit val tasksReads = new Reads[Graph[Task, DiEdge]] {
def reads(json: JsValue): JsResult[Graph[Task, DiEdge]] = {
try {
val graph = Graph.fromJson[Task, DiEdge](json.toString, quickJson)
JsSuccess(graph)
}
catch {
case e: Exception =>
JsError(e.toString)
}
}
}
implicit def stuffModelFormat = Jsonx.formatCaseClass[Stuff]
You can try writing companion objects for yours case classes where you specify the formatting.
Example:
object Task {
implicit val taskModelFormat = Json.format[Task]
}
object Stuff {
implicit val staffModelFormat = Json.format[Stuff]
}
instead of the above implicits. With this solution compiler will resolve the known formatters for you and you could be only required to specify the missing/unknown types instead of the whole structure.

How to convert Future[Seq[SomeClass]] to Json?

In Play framework I'm using Slick with MySQL database, how could I convert query result (Future[Seq[SomeClass]]) to Json for further usage in jQuery Autocomplete. I can serialize SomeClass , but where should I use .map(or something else) ?
Added:
Model:
package models
import play.api.libs.json._
case class Equipment(id: Long, name: String,area: String,kiMin: Double,kiMax: Double,cosFiMin: Double,cosFiMax: Double){
implicit val equipmentWrites = new Writes[Equipment] {
def writes(equipment: Equipment) = Json.obj(
"id" -> equipment.id,
"name" -> equipment.name,
"area" -> equipment.area,
"kiMin" -> equipment.kiMin,
"kiMax" -> equipment.kiMax,
"cosFiMin" -> equipment.cosFiMin,
"cosFiMax" -> equipment.cosFiMax
)
//also tried this for Seq
/* def writes(equipment: Equipment): JsValue = {
val equipmentSeq = Seq(
"id" -> JsNumber(equipment.id),
"name" -> JsString(equipment.name),
"area" -> JsString(equipment.area),
"kiMin" -> JsNumber(equipment.kiMin),
"kiMax" -> JsNumber(equipment.kiMax),
"cosFiMin" -> JsNumber(equipment.cosFiMin),
"cosFiMax" -> JsNumber(equipment.cosFiMax)
)
JsObject(equipmentSeq)
}*/
}
}
Controller:
def auto(term: String) = Action {
Ok(Json.toJson(equipmentDAO.get(term)))
}
DAO:
def get(name: String): Future[Seq[Equipment]] = db.run((equipment.filter { _.name === name }).result)
Added2:
controller method:
def auto(term: String) = Action.async {
val future: Future[Seq[Equipment]] = equipmentDAO.get(term)
future.map { seqOfSomeClass =>
Ok(Json.toJson(seqOfSomeClass))
}
}
Serializer:
implicit val equipmentWrites: Writes[Equipment] = (
(JsPath \ "id").write[Long] and
(JsPath \ "name").write[String] and
(JsPath \ "area").write[String] and
(JsPath \ "kiMin").write[Double] and
(JsPath \ "kiMax").write[Double] and
(JsPath \ "cosFiMin").write[Double] and
(JsPath \ "cosFiMax").write[Double]
)(unlift(Equipment.unapply))
Considering that you understand how Play Framework handles JSON and already have Reads and Writes for SomeClass, you can do something like this in your controller:
def someAction = Action.async {
val future: Future[Seq[SomeClass]] = ??? // get the future using Slick
future.map { seqOfSomeClass =>
Ok(Json.toJson(seqOfSomeClass))
}
}
This will serve a list of SomeClass in JSON.

Applying conversion to play framework json element before applying to class

I have a class as so
case class Token(
creationDate: Date,
expires: Option[Date]
) {
def toJson(): String = Json.stringify(Json.toJson(this))
}
object Token {
def fromJson(json: String): Token = Json.parse(json).as[Token]
implicit val tokenReads: Reads[Token] = (
(JsPath \ "creation_date").read[Date] and
(JsPath \ "expires").readNullable[Date]
) (Token.apply _)
implicit val tokenWrites: Writes[Token] = (
(JsPath \ "creation_date").write[Date] and
(JsPath \ "expires").writeNullable[Date]
) (unlift(Token.unapply))
}
Which gets created from json like
{
"creation_date": "2014-05-22T08:05:57.556385+00:00",
"expires": null
}
Problem is that date format cant be converted to a date directly, I am looking to somehow take that string, and convert it using
DateFormat df2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
String string2 = "2001-07-04T12:08:56.235-07:00";
Date result2 = df2.parse(string2);
and then pass it into the Token constructor, but I cant seem to figure out how to do that in the apply function
You can map String to Date if you have special date format
def strToDate(string2: String): Date = {
//... something such
val df2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
df2.parse(string2);
}
implicit val tokenReads: Reads[Token] = (
(JsPath \ "creation_date").read[String].map(strToDate) and
(JsPath \ "expires").readNullable[String].map(_.map(strToDate))
) (Token.apply _)

Play 2 Json format, capture Int or String

I have a problem, I use an rest webservice than return an json not well formatted, sometimes return a string sometimes an integer in the same field. This is the code of the format:
implicit val ItemFormat: Format[Item] = (
(JsPath \ "a").format[String] and
(JsPath \ "b").format[String] and
(JsPath \ "c").formatNullable[String]
)(Item.apply , unlift(Item.unapply))
If c is empty or not exist or is a string works well, but if the c is an integer I have this error:
ValidationError(List(error.expected.jsstring),WrappedArray()))
I would obtain, if c is an integer, or convert it in a string or put c=None
You can do it this way.
case class Item(a: String, b: String, c: Option[String])
implicit val reads: Reads[A] = new Reads[A] {
override def reads(json: JsValue): JsResult[A] = {
for {
a <- (json \ "a").validate[String]
b <- (json \ "b").validate[String]
} yield {
val cValue = (json \ "c")
val cOptString = cValue.asOpt[String].orElse(cValue.asOpt[Int].map(_.toString))
Item(a, b, cOptString)
}
}
}