Play框架2.3中的隐式JSON转换导致空指针异常

14

我正在尝试将JSON解析为我的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的问题和解析尝试

val str = """{"limit":10,"filter":{"date":"2014-10-27"}}"""
val frm = Json.parse(str).as[DealFormMap]

引起了一个神秘的错误堆栈,我似乎无法解决

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]

我已经没有更多的想法了,可能的问题是什么?

2个回答

34

问题在于初始化顺序。dealFormMapReads 依赖于隐式的 dealFormFilterMapReads,而后者直到之后才被定义。因为隐式的依赖被找到了,所以代码会编译通过,尽管它还没有被初始化,所以 dealFormMapReads 被读作 null,最终导致了 NPE。

进行延迟加载将解决此问题:

implicit val dealFormMapReads: Reads[DealFormMap] = (
     (JsPath \ "limit").readNullable[Int] and
     (JsPath \ "filter").lazyReadNullable[DealFormFilterMap](dealFormFilterMapReads)
)(DealFormMap)

或者你可以交换定义 Reads 的顺序。


这里抛出的 NPE 与此示例类似:

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`

4

问题在于LimbSoup所提到的顺序。但值得注意的是,使用Json宏,您可以实现相同的结果。

import play.api.libs.json._

implicit val dealFormFilterMapReads: Reads[DealFormFilterMap] = Json.reads[DealFormFilterMap]

implicit val dealFormMapReads: Reads[DealFormMap] = Json.reads[DealFormMap]

请注意,使用此宏时,如果您更改顺序
implicit val dealFormMapReads: Reads[DealFormMap] = Json.reads[DealFormMap]

implicit val dealFormFilterMapReads: Reads[DealFormFilterMap] = Json.reads[DealFormFilterMap]   

您将收到以下有用的警告提示:
 Reference to uninitialized value dealFormFilterMapReads
[warn] implicit val dealFormMapReads: Reads[DealFormMap] = Json.reads[DealFormMap]
[warn]                                                                 ^
[warn] one warning found

如LimpSoup提到的那样,您可以通过使dealFormFilterMapReads变为lazy或重新排序来解决这个问题。

免责声明

在Play中使用Json宏嵌套需要Scala 2.10,并且仅支持2.1.0之后的版本。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接