Play 2.3 implicit json conversion causes null pointer exception

前提是你 提交于 2019-12-30 03:58:05

问题


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?


回答1:


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`



回答2:


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



来源:https://stackoverflow.com/questions/26590918/play-2-3-implicit-json-conversion-causes-null-pointer-exception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!