Play 2.3 implicit json conversion causes null pointer exception - json

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

Related

Playframework Akka WebSocket.accept How to use derived classes and MessageFlowTransformer

Using Playframework 2.5, Akka and Websockets I would like to be able to declare a websocket that accepts different incoming and outcoming classes, those classes would have a base class (one base class for incoming and another one for outcoming messages). Here's where I am :
object IncomingMessage {
val subscribeMessage = "subscribe"
val unsubscribeMessage = "unsubscribe"
}
abstract class IncomingMessage(action: String)
case class SubscribeMessage(repository: String, interval: Int, action: String = IncomingMessage.subscribeMessage) extends IncomingMessage(action)
case class UnsubscribeMessage(repository: String, action: String = IncomingMessage.unsubscribeMessage) extends IncomingMessage(action)
object SubscribeMessage {
implicit val SubscribeMessageWrites: Reads[SubscribeMessage] = (
(JsPath \ "repository").read[String] and
(JsPath \ "interval").read[Int](min(1)) and
(JsPath \ "action").read[String]
)(SubscribeMessage.apply _)
}
object UnsubscribeMessage {
implicit val UnsubscribeMessageWrites: Reads[UnsubscribeMessage] = (
(JsPath \ "repository").read[String] and
(JsPath \ "action").read[String]
)(UnsubscribeMessage.apply _)
}
The outcoming messages are based on the same principal so I will not show them.
Reading the Play documentation I would implement something like this
import play.api.libs.json._
import play.api.mvc.WebSocket.FrameFormatter
implicit val incomingFormat = Json.format[IncomingMessage]
implicit val outcomingFormat = Json.format[OutcomingMessage]
implicit val messageFlowTransformer = MessageFlowTransformer.jsonMessageFlowTransformer[IncomingMessage, OutcomingMessage]
...
def socket = WebSocket.acceptOrResult[IncomingMessage, OutcomingMessage] { ... }
I would like to use formatters and the MessageFlowTransformer to be able to send messages without transforming them to json explicitly, so I don't want to write :
out ! Json.toJson(outcomingMessage)
But I would like to write
out ! outcomingMessage
But I get back an error during compile time for the implicit val incomingFormat AND the implicit val outcomingFormat
No unapply or unapplySeq function found
How can I solve this issue ?
Is it possible to use derived classes as websocket messages using actors ?

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

Need json deserializer for 3rd-party case class

I'm implementing a SecureSocial service for my Scala Play! application. I'm using the ReactiveMongoPlugin to access the MongoDB store. Here is the code...
lazy val users: JSONCollection = ReactiveMongoPlugin.db.collection[JSONCollection]("users")
def find(providerId: String, userId: String): Future[Option[BasicProfile]] = {
users
.find(Json.arr(Json.obj("providerId" -> providerId), Json.obj("userId" -> userId)))
.cursor[BasicProfile]
.headOption
}
BasicProfile is a SecureSocial case class that does not implement the json serializer/deserializer. So predictably I'm getting...
No Json deserializer found for type securesocial.core.BasicProfile
I know how to implement reads/writes for my own case classes but I'm too new to Scala to know how to do this for library case classes like BasicProfile. How can I add json read/write to BasicProfile?
Adding Reads and Writes to library case classes is really similar to adding them to your own classes. You will need to create a Read and a Write for each library case class. For example, let's say we had these case classes:
case class Example(a: String, b: ExampleB)
case class ExampleB(c: Int)
They should be parsed using these:
implicit val exampleWrites: Writes[Example] = (
(JsPath \ "a").write[String] and
(JsPath \ "b").write[ExampleB]
)
implicit val exampleBWrites: Writes[ExampleB] = (
(JsPath \ "c").write[Int]
)
implicit val exampleReads: Reads[Example] = (
(JsPath \ "a").read[String] and
(JsPath \ "b").read[ExampleB]
)
implicit val exampleBReads: Reads[ExampleB] = (
(JsPath \ "c").read[Int]
)
Since these Reads and Writes are implicit, importing them should automatically make them work where you need them.
See the documentation for more information.

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.

Defaults for missing properties in play 2 JSON formats

I have an equivalent of the following model in play scala :
case class Foo(id:Int,value:String)
object Foo{
import play.api.libs.json.Json
implicit val fooFormats = Json.format[Foo]
}
For the following Foo instance
Foo(1, "foo")
I would get the following JSON document:
{"id":1, "value": "foo"}
This JSON is persisted and read from a datastore. Now my requirements have changed and I need to add a property to Foo. The property has a default value :
case class Foo(id:String,value:String, status:String="pending")
Writing to JSON is not a problem :
{"id":1, "value": "foo", "status":"pending"}
Reading from it however yields a JsError for missing the "/status" path.
How can I provide a default with the least possible noise ?
(ps: I have an answer which I will post below but I am not really satisfied with it and would upvote and accept any better option)
Play 2.6+
As per #CanardMoussant's answer, starting with Play 2.6 the play-json macro has been improved and proposes multiple new features including using the default values as placeholders when deserializing :
implicit def jsonFormat = Json.using[Json.WithDefaultValues].format[Foo]
For play below 2.6 the best option remains using one of the options below :
play-json-extra
I found out about a much better solution to most of the shortcomings I had with play-json including the one in the question:
play-json-extra which uses [play-json-extensions] internally to solve the particular issue in this question.
It includes a macro which will automatically include the missing defaults in the serializer/deserializer, making refactors much less error prone !
import play.json.extra.Jsonx
implicit def jsonFormat = Jsonx.formatCaseClass[Foo]
there is more to the library you may want to check: play-json-extra
Json transformers
My current solution is to create a JSON Transformer and combine it with the Reads generated by the macro. The transformer is generated by the following method:
object JsonExtensions{
def withDefault[A](key:String, default:A)(implicit writes:Writes[A]) = __.json.update((__ \ key).json.copyFrom((__ \ key).json.pick orElse Reads.pure(Json.toJson(default))))
}
The format definition then becomes :
implicit val fooformats: Format[Foo] = new Format[Foo]{
import JsonExtensions._
val base = Json.format[Foo]
def reads(json: JsValue): JsResult[Foo] = base.compose(withDefault("status","bidon")).reads(json)
def writes(o: Foo): JsValue = base.writes(o)
}
and
Json.parse("""{"id":"1", "value":"foo"}""").validate[Foo]
will indeed generate an instance of Foo with the default value applied.
This has 2 major flaws in my opinion:
The defaulter key name is in a string and won't get picked up by a refactoring
The value of the default is duplicated and if changed at one place will need to be changed manually at the other
The cleanest approach that I've found is to use "or pure", e.g.,
...
((JsPath \ "notes").read[String] or Reads.pure("")) and
((JsPath \ "title").read[String] or Reads.pure("")) and
...
This can be used in the normal implicit way when the default is a constant. When it's dynamic, then you need to write a method to create the Reads, and then introduce it in-scope, a la
implicit val packageReader = makeJsonReads(jobId, url)
An alternative solution is to use formatNullable[T] combined with inmap from InvariantFunctor.
import play.api.libs.functional.syntax._
import play.api.libs.json._
implicit val fooFormats =
((__ \ "id").format[Int] ~
(__ \ "value").format[String] ~
(__ \ "status").formatNullable[String].inmap[String](_.getOrElse("pending"), Some(_))
)(Foo.apply, unlift(Foo.unapply))
I think the official answer should now be to use the WithDefaultValues coming along Play Json 2.6:
implicit def jsonFormat = Json.using[Json.WithDefaultValues].format[Foo]
Edit:
It is important to note that the behavior differs from the play-json-extra library. For instance if you have a DateTime parameter that has a default value to DateTime.Now, then you will now get the startup time of the process - probably not what you want - whereas with play-json-extra you had the time of the creation from the JSON.
I was just faced with the case where I wanted all JSON fields to be optional (i.e. optional on user side) but internally I want all fields to be non-optional with precisely defined default values in case the user does not specify a certain field. This should be similar to your use case.
I'm currently considering an approach which simply wraps the construction of Foo with fully optional arguments:
case class Foo(id: Int, value: String, status: String)
object FooBuilder {
def apply(id: Option[Int], value: Option[String], status: Option[String]) = Foo(
id getOrElse 0,
value getOrElse "nothing",
status getOrElse "pending"
)
val fooReader: Reads[Foo] = (
(__ \ "id").readNullable[Int] and
(__ \ "value").readNullable[String] and
(__ \ "status").readNullable[String]
)(FooBuilder.apply _)
}
implicit val fooReader = FooBuilder.fooReader
val foo = Json.parse("""{"id": 1, "value": "foo"}""")
.validate[Foo]
.get // returns Foo(1, "foo", "pending")
Unfortunately, it requires writing explicit Reads[Foo] and Writes[Foo], which is probably what you wanted to avoid? One further drawback is that the default value will only be used if the key is missing or the value is null. However if the key contains a value of the wrong type, then again the whole validation returns a ValidationError.
Nesting such optional JSON structures is not a problem, for instance:
case class Bar(id1: Int, id2: Int)
object BarBuilder {
def apply(id1: Option[Int], id2: Option[Int]) = Bar(
id1 getOrElse 0,
id2 getOrElse 0
)
val reader: Reads[Bar] = (
(__ \ "id1").readNullable[Int] and
(__ \ "id2").readNullable[Int]
)(BarBuilder.apply _)
val writer: Writes[Bar] = (
(__ \ "id1").write[Int] and
(__ \ "id2").write[Int]
)(unlift(Bar.unapply))
}
case class Foo(id: Int, value: String, status: String, bar: Bar)
object FooBuilder {
implicit val barReader = BarBuilder.reader
implicit val barWriter = BarBuilder.writer
def apply(id: Option[Int], value: Option[String], status: Option[String], bar: Option[Bar]) = Foo(
id getOrElse 0,
value getOrElse "nothing",
status getOrElse "pending",
bar getOrElse BarBuilder.apply(None, None)
)
val reader: Reads[Foo] = (
(__ \ "id").readNullable[Int] and
(__ \ "value").readNullable[String] and
(__ \ "status").readNullable[String] and
(__ \ "bar").readNullable[Bar]
)(FooBuilder.apply _)
val writer: Writes[Foo] = (
(__ \ "id").write[Int] and
(__ \ "value").write[String] and
(__ \ "status").write[String] and
(__ \ "bar").write[Bar]
)(unlift(Foo.unapply))
}
This probably won't satisfy the "least possible noise" requirement, but why not introduce the new parameter as an Option[String]?
case class Foo(id:String,value:String, status:Option[String] = Some("pending"))
When reading a Foo from an old client, you'll get a None, which I'd then handle (with a getOrElse) in your consumer code.
Or, if you don't like this, introduce an BackwardsCompatibleFoo:
case class BackwardsCompatibleFoo(id:String,value:String, status:Option[String] = "pending")
case class Foo(id:String,value:String, status: String = "pending")
and then turn that one into a Foo to work with further on, avoiding to have to deal with this kind of data gymnastics all along in the code.
You may define status as an Option
case class Foo(id:String, value:String, status: Option[String])
use JsPath like so:
(JsPath \ "gender").readNullable[String]