Воспроизведение 2.3 неявное преобразование json вызывает исключение из null-указателя
Я пытаюсь проанализировать json в классе case 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]
У меня заканчиваются идеи, что может быть проблемой?
Ответы
Ответ 1
Проблема заключается в порядке инициализации. 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`
Ответ 2
Проблема заключается в порядке, о котором упоминается 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
ленивым или переупорядочивающим, что имеет больше смысла в этом случае.
Отказ
С помощью создания макроса Json в игре требуется Scala 2.10
и поддерживается в версиях Play после 2.1.0