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.
Using Json macro inception in play needs Scala 2.10
and is supported on Play versions after 2.1.0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With