Play Framework 2 JSON Reads, deserialization of one variable - json

I'm using Play Framework 2.4 and I'm trying to do a basic JSON deserialization with Reads but I get an error. Here is the code:
case class Config(action: String)
and somewhere,
implicit val configReads: Reads[Config] = (
(__ \ "action").read[String]
)(Config.apply _)
I think the configReads is correctly formed but I get an IDE error on the "read" method call (symbol not defined), when I compile the code I get the following error:
Error:(30, 27) overloaded method value read with alternatives:
(t: String)play.api.libs.json.Reads[String] <and>
(implicit r: play.api.libs.json.Reads[String])play.api.libs.json.Reads[String]
cannot be applied to (String => wings.common.json.Config)
(__ \ "action").read[String]
^
but, if instead of trying to deserialize ONE argument I declare a class with TWO arguments in the constructor and I write the code to deserialize it, it works.
Does anybody know how to solve this?
Edit:
Digging in the depth of Google I found this for Play 2.1.x but I'm using the Json library for Play 2.4.1 so this problem should not be happening.

You can do like this:
implicit val configReads: Reads[Config] = (
(__ \ "action").read[String]
) map Config.apply

Related

Play Scala JSON: combine properties

I have the following case class:
case class User(name: String).
I am trying to implement a JSON Reads converter for it, so I can do the following:
val user = userJson.validate[User]
… but the incoming JSON has slightly different structure:
{ "firstName": "Bob", "lastName": "Dylan" }.
How can I implement my JSON Reads converter to combine the JSON fields firstName and lastName into a name property on my class?
This should do the trick:
implicit val userReads: Reads[User] =
for {
first <- (__ \ "firstName").read[String]
last <- (__ \ "lastName").read[String]
} yield User(s"$first $last")
EDIT
Without using a for comprehension
implicit val userReads =
{
(__ \ "firstName").read[String] and
(__ \ "lastName"
}.read[String] ).tupled.map(t => User(s"${t._1} ${t._2}"))
Bringing userReads in scope where you want to use it will let you parse the JSON you provided.
Reads is essentially a function from JsValue to JsResult, meaning userReads represents a function from JsValue -> JsResult. Within the function, it first inspects the provided JSON & tries to read out a property named "firstName" from the current JSON path (__ is shorthand for this). \ indicates that the field its looking for is one level beneath the root, and read[String] means the value associated with the "firstName" key should be read as a string. Same follows for "lastName".
Edit
In the version without the for comprehension, it first creates an intermediary object FunctionalBuilder[Reads]#CanBuild[String, String], which is a complicated way of saying it reads two distinct strings from the Json. Next it converts that complex object into a Reads[(String, String)] by way of tupled. Finally it maps the pair of strings into a User.
Were you to try validating some JSON without "firstName" & "lastName", this will fail with a validation error for a missing path.

What is this "and" in ScalaJsonCombinator (when defining a Writes)?

I've been using this json combinator for several basic / standard cases without really understanding how it works. All was fine.
Now I want to get myself prepared for whatever advanced cases might come; I need to understand the code.
Ref.: https://www.playframework.com/documentation/2.3.x/ScalaJsonCombinators
I think I can understand the Reads:
implicit val locationReads: Reads[Location] = (
(JsPath \ "lat").read[Double] and
(JsPath \ "long").read[Double]
)(Location.apply _)
It creates a Reads that:
First -- when given a JsValue (through its "reads" method) -- it pulls the "lat", followed by the "long". Out of those two it creates a tuple (Double, Double). -- https://www.playframework.com/documentation/2.3.x/api/scala/index.html#play.api.libs.json.Reads
That tuple is then assigned to the partial function of that Reads..., which in this case is whatever returned by "Location.apply _". I tried it in repl:
...
scala> val yowMan = Location.apply _
yowMan: (Double, Double) => Location = <function2>
scala> yowMan(1, 2)
res14: Location = Location(1.0,2.0)
That partial function takes the a tuple of (Double, Double) as input. So..., the outcome of step 1 is channeled to step 2, and we get an instance of Location as the return of "reads".
Now for the Writes:
implicit val locationWrites: Writes[Location] = (
(JsPath \ "lat").write[Double] and
(JsPath \ "long").write[Double]
)(unlift(Location.unapply))
First the "unapply". I tried in repl:
scala> val heyDude = Location.unapply
<console>:16: error: missing arguments for method unapply in object Location;
follow this method with `_' if you want to treat it as a partially applied function
val heyDude = Location.unapply
Oops, ok, I followed the instruction:
scala> val heyDude = Location.unapply _
heyDude: Location => Option[(Double, Double)] = <function1>
Ok, so we get a partial function that transforms an instance of Location to an (optional) tuple of (Double, Double).
Next, the "unlift":
scala> val hohoho = unlift(heyDude)
hohoho: Location => (Double, Double) = <function1>
scala> val loc = Location(1, 2)
loc: Location = Location(1.0,2.0)
scala> hohoho(loc)
res16: (Double, Double) = (1.0,2.0)
Ok, so... unlift simply throws away the "Option", and takes us directly to the tuple.
Ok... so... I guess... this "writes" of the Writes... *) when given an instance of Location, it will:
Pass that object through that partial function produced by unlift(Location.unapply).
The tuple (Double, Double) returned by that partial function is then channeled to whatever is produced by this:
(JsPath \ "lat").write[Double] and
(JsPath \ "long").write[Double]
What exactly is that "whatever"? Following the API doc of JsPath, I think it is OWrites: https://www.playframework.com/documentation/2.3.x/api/scala/index.html#play.api.libs.json.OWrites
But... I can't see there's a method named "and" in OWrites. Where is this "and" declared? And what does it do? Is it: "oWrites1 and oWrites2" produces "oWrites3"? And this "oWrites3" is a special type of OWrites that takes tuple as input? ... If that's the case... the tuple doesn't have information about the name of the property in the case class ("lat" and "long"). How does it know that the produced json string should be {"lat": 1, "long": 2} then?
Sorry for the train of questions. Please help me obtaining a clear understanding of this. Thanks!
*) https://www.playframework.com/documentation/2.3.x/api/scala/index.html#play.api.libs.json.Writes
UPDATES:
Adding related question: Syntax and meaning of a Scala/Play! code sample
When in doubt, decompile it. This showed that there is an implicit toFunctionalBuilderOps, which then you can see in FunctionalBuilderOps that there is your and method

Play 2.3 implicit json conversion causes null pointer exception

I'm trying to parse json into my case class DealFormMap
case class DealFormMap(limit: Option[Int], filter: Option[DealFormFilterMap])
case class DealFormFilterMap(date: Option[String], code: Option[String])
implicit val dealFormMapReads: Reads[DealFormMap] = (
(JsPath \ "limit").readNullable[Int] and
(JsPath \ "filter").readNullable[DealFormFilterMap]
)(DealFormMap)
implicit val dealFormFilterMapReads: Reads[DealFormFilterMap] = (
(JsPath \ "date").readNullable[String] and
(JsPath \ "code").readNullable[String]
)(DealFormFilterMap)
JSON in question and parsing attempt
val str = """{"limit":10,"filter":{"date":"2014-10-27"}}"""
val frm = Json.parse(str).as[DealFormMap]
causes a cryptic error stack that I just can't seem to crack
play.api.Application$$anon$1: Execution exception[[NullPointerException: null]]
at play.api.Application$class.handleError(Application.scala:296) ~[play_2.11-2.3.5.jar:2.3.5]
at play.api.DefaultApplication.handleError(Application.scala:402) [play_2.11-2.3.5.jar:2.3.5]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$14$$anonfun$apply$1.applyOrElse(PlayDefaultUpstreamHandler.scala:205) [play_2.11-2.3.5.jar:2.3.5]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$14$$anonfun$apply$1.applyOrElse(PlayDefaultUpstreamHandler.scala:202) [play_2.11-2.3.5.jar:2.3.5]
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36) [scala-library-2.11.2.jar:na]
Caused by: java.lang.NullPointerException: null
at play.api.libs.json.PathReads$$anonfun$nullable$1$$anonfun$apply$7$$anonfun$apply$9.apply(JsConstraints.scala:65) ~[play-json_2.11-2.3.5.jar:2.3.5]
at play.api.libs.json.PathReads$$anonfun$nullable$1$$anonfun$apply$7$$anonfun$apply$9.apply(JsConstraints.scala:63) ~[play-json_2.11-2.3.5.jar:2.3.5]
at play.api.libs.json.JsResult$class.fold(JsResult.scala:76) ~[play-json_2.11-2.3.5.jar:2.3.5]
at play.api.libs.json.JsSuccess.fold(JsResult.scala:9) ~[play-json_2.11-2.3.5.jar:2.3.5]
at play.api.libs.json.PathReads$$anonfun$nullable$1$$anonfun$apply$7.apply(JsConstraints.scala:61) ~[play-json_2.11-2.3.5.jar:2.3.5]
I'm running out of ideas here, what could be the problem?
The problem is the initialization order. dealFormMapReads depends on the implicit dealFormFilterMapReads, which isn't defined until after. It will compile because the implicit is found, even though it hasn't been initialized, so dealFormMapReads is read as null, which eventually causes the NPE.
Lazily loading will fix it:
implicit val dealFormMapReads: Reads[DealFormMap] = (
(JsPath \ "limit").readNullable[Int] and
(JsPath \ "filter").lazyReadNullable[DealFormFilterMap](dealFormFilterMapReads)
)(DealFormMap)
Or you could just swap the order in which the Reads are defined.
The NPE thrown here is similar to this example:
case class A(i: Int)
object Test {
val test = a.toString
val a = A(1)
}
// Compiles up to here
Test.test // throws NPE, because `a` was not initialized before `test`
The problem is the order as mentioned by LimbSoup. But it's worth noting that with using Json macros you can achieve the same result with
import play.api.libs.json._
implicit val dealFormFilterMapReads: Reads[DealFormFilterMap] = Json.reads[DealFormFilterMap]
implicit val dealFormMapReads: Reads[DealFormMap] = Json.reads[DealFormMap]
Note that by using this macros, if you change the order
implicit val dealFormMapReads: Reads[DealFormMap] = Json.reads[DealFormMap]
implicit val dealFormFilterMapReads: Reads[DealFormFilterMap] = Json.reads[DealFormFilterMap]
you will get the following helpful warning:
Reference to uninitialized value dealFormFilterMapReads
[warn] implicit val dealFormMapReads: Reads[DealFormMap] = Json.reads[DealFormMap]
[warn] ^
[warn] one warning found
which you can fix (again as mentioned by LimpSoup) by making dealFormFilterMapReads lazy or reordering which makes more sense in this case.
Disclaimer
Using Json macro inception in play needs Scala 2.10 and is supported on Play versions after 2.1.0

Json Scala object serialization in Play2.2.1 framework

So, I've just recently started learning Scala. Sorry for my incompetence in advance.
I tried to look up my answer on stackoverflow. I was able to find several related topics, but I didn't spot my problem.
I'm trying to send a json response based on a Scala object. I have an Action and I'm doing the following:
def oneCredential = Action {
val cred = Credential("John", "Temp", "5437437")
Ok(Json.toJson(cred))
}
I've created a case class and appropriate implicit Writes[T] for it
import play.api.libs.json._
import play.api.libs.functional.syntax._
import play.api.libs.json.util._
case class Credential(name: String, account: String, password: String)
object Credential{
implicit val credentialWrites = (
(__ \ "name").write[String] and
(__ \ "account").write[String] and
(__ \ "password").write[String]
)(Credential)
}
When I'm trying to run this, I've the following error: "Overloaded method value [apply] cannot be applied to (models.Credential.type)". Also, I tried this
implicit val credentialWrites = (
(__ \ "name").write[String] and
(__ \ "account").write[String] and
(__ \ "password").write[String]
)(Credential.apply _)
Fail. The error: could not find implicit value for parameter fu: play.api.libs.functional.Functor[play.api.libs.json.OWrites]
Then this:
implicit val credentialWrites = (
(__ \ "name").writes[String] and
(__ \ "account").writes[String] and
(__ \ "password").writes[String]
)(Credential)
Another fail: "value writes is not a member of play.api.libs.json.JsPath Note: implicit value credentialWrites is not applicable here because it comes after the application point and it lacks an explicit result type". Right, I understood the first part of an error, but not the second.
Finally I found a shorthand solution:
implicit val credentialWrites = Json.writes[Credential]
With this I've got no errors and the code finally worked. I've found the solution on this blog. It's said that the shorthand form is exactly the same as the one with "writes" above. But this "long" form didn't work for me.
Why is shorthand version working, while the long one isn't? Can somebody explain this?
Thank you!
PS Scala version: 2.10.2
The definitions you've given would work for Reads, but Writes needs a different kind of argument at the end. Take the following example:
case class Baz(foo: Int, bar: String)
val r = (__ \ 'foo).read[Int] and (__ \ 'bar).read[String]
val w = (__ \ 'foo).write[Int] and (__ \ 'bar).write[String]
r can be applied to a function (Int, String) => A to get a Reads[A], which means we can use it as follows (these are all equivalent):
val bazReader1 = r((foo: Int, bar: String) => Baz(foo, bar))
val bazReader2 = r(Baz.apply _)
val bazReader3 = r(Baz)
What we're doing is lifting the function into the applicative functor for Reads so that we can apply it to our Reads[Int] and Reads[String] (but you don't need to care about that if you don't want to).
w takes a different kind of argument (again, you don't need to care, but this is because Writes has a contravariant functor—it doesn't have an applicative functor):
val bazWriter1 = w((b: Baz) => (b.foo, b.bar))
We could write this equivalently as the following:
val bazWriter2 = w(unlift(Baz.unapply))
Here we're using the case class's automatically generated extractor, unapply, which returns an Option[(Int, String)]. We know in this case that it'll always return a Some, so we can use unlift (which comes from the functional syntax package, and just calls the standard library's Function.unlift) to turn the Baz => Option[(Int, String)] into the required Baz => (Int, String).
So just change your final line to )(unlift(Credential.unapply)) and you're good to go.

implicit json read and writes for lists

I am following the following example https://github.com/leon/play-salat/tree/master/sample . In my data model I have lists and I tried to adopt the model to that.
I get the following compilation errors:
[error] /home/malte/workspace/bdt/app/models/Ballot.scala:26: No Json deserializer found for type List[models.Position]. Try to implement an implicit Writes or Format for this type.
[error] "positions" -> b.positions,
[error] ^
[error] /home/malte/workspace/bdt/app/models/Ballot.scala:33: No Json deserializer found for type List[models.Position]. Try to implement an implicit Reads or Format for this type.
[error] (__ \ 'values).read[List[Position]]).map { l => l.getOrElse(Nil) } ~
For the type "Position" I have implicit reads and writes, but how to make it for List[Position] ? My implicit write and read:
implicit val ballotJsonWrite = new Writes[Ballot] {
def writes(b: Ballot): JsValue = {
Json.obj(
"positions" -> b.positions,
"title" -> b.title)
}
}
implicit val ballotJsonRead = (
(__ \ 'positions).readNullable(
(__ \ 'values).read[List[Position]]).map { l => l.getOrElse(Nil) } ~
(__ \ 'title).read[String])(Ballot.apply _)
For the read I followed Create implicit json read for List collection which might be missing from input json , but I get the above mentioned error.
actually now I found out that in the latest versions of salat. Json is automatically used to move between models and json objects. I removed the whole part and no it runs smoothly.
https://github.com/novus/salat/wiki/JSON