scala 中的隐式 json 转换器在运行时为 null
implicit json converter in scala is null at runtime
使用过的播放框架我有 case classes 的文件(在项目中有聚合任务。这很重要)像
case class Project (var tasks: Map[String, Task])
case class Task (var activities: Map[String, String])
并在另一个文件中 trait 用于转换
trait JsonConverters {
implicit val projectWrite: OWrites[Project] = Json.writes[Project]
implicit val projectRead: Reads[Project] = Json.reads[Project]
implicit val taskWrite: OWrites[Task] = Json.writes[Task]
implicit val taskRead: Reads[Task] = Json.reads[Task]
}
object JsonConverters extends JsonConverters
在我的服务中 class 类似
import support.JsonConverters._
class Service {
def someMethod(json: String) = {
val obj = Json.parse(json).as[Map[String, Project]]
}
}
在编译时一切正常。但是在 json 解析中我在 json 库中得到了 NPE
(play.api.libs.json.DefaultReads#mapReads
)
implicit def mapReads[K, V](k: String => JsResult[K])(implicit fmtv: Reads[V]): Reads[Map[K, V]] = Reads[Map[K, V]] {
implicit fmtv: Reads[V]
对于任务 class 为空(对于项目一切正常)
我认为这是因为在 .as[Map[String, Project]
中只指定了项目 class,但没有指定任务。但不完全是
我找到了 2 种不同的解决方案来解决这个问题,但对我来说都很难看
1) 在 相同的 文件中声明所有隐式转换器,大小写为 classes。但我想将所有转换器放在单独的文件中。这个解决方案不适合我
2) 在 trait JsonConverters
中使用 lazy mod 为任务声明隐式
trait JsonConverter {
implicit val projectWrite: OWrites[Project] = Json.writes[Project]
implicit val projectRead: Reads[Project] = Json.reads[Project]
implicit lazy val taskWrite: OWrites[Task] = Json.writes[Task]
implicit lazy val taskRead: Reads[Task] = Json.reads[Task]
}
它工作正常,看起来也不错。但是我不明白为什么在单独的特征中没有惰性它就不能工作?谁能描述一下?或建议其他解决方案
普通变量(val
)和惰性变量(lazy val
)的初始化顺序不同。
val
按照文件中描述的顺序进行了初始化。
lazy val
在首次访问时 被初始化 。
在您的情况下,Project
包含一个 Task
,因此 projectRead
需要初始化 taskRead
才能完成。
在您的代码中,如果没有 lazy
,projectRead
在 taskRead
之前声明,因此当 projectRead
需要时 taskRead
仍然是 null
它。这就是你得到 NPE 的原因。
仍然,编译器可以找到隐式 taskRead
并且不会引发错误。
您对 lazy
的修复有效,因为 taskRead
现在在首次访问 时初始化 。现在,taskRead
在 projectRead
请求时初始化。 NPE 消失了。
您可以通过切换声明顺序来实现相同的修复:
trait JsonConverter {
implicit val taskWrite: OWrites[Task] = Json.writes[Task]
implicit val taskRead: Reads[Task] = Json.reads[Task]
implicit val projectWrite: OWrites[Project] = Json.writes[Project]
implicit val projectRead: Reads[Project] = Json.reads[Project]
}
使用过的播放框架我有 case classes 的文件(在项目中有聚合任务。这很重要)像
case class Project (var tasks: Map[String, Task])
case class Task (var activities: Map[String, String])
并在另一个文件中 trait 用于转换
trait JsonConverters {
implicit val projectWrite: OWrites[Project] = Json.writes[Project]
implicit val projectRead: Reads[Project] = Json.reads[Project]
implicit val taskWrite: OWrites[Task] = Json.writes[Task]
implicit val taskRead: Reads[Task] = Json.reads[Task]
}
object JsonConverters extends JsonConverters
在我的服务中 class 类似
import support.JsonConverters._
class Service {
def someMethod(json: String) = {
val obj = Json.parse(json).as[Map[String, Project]]
}
}
在编译时一切正常。但是在 json 解析中我在 json 库中得到了 NPE
(play.api.libs.json.DefaultReads#mapReads
)
implicit def mapReads[K, V](k: String => JsResult[K])(implicit fmtv: Reads[V]): Reads[Map[K, V]] = Reads[Map[K, V]] {
implicit fmtv: Reads[V]
对于任务 class 为空(对于项目一切正常)
我认为这是因为在 .as[Map[String, Project]
中只指定了项目 class,但没有指定任务。但不完全是
我找到了 2 种不同的解决方案来解决这个问题,但对我来说都很难看
1) 在 相同的 文件中声明所有隐式转换器,大小写为 classes。但我想将所有转换器放在单独的文件中。这个解决方案不适合我
2) 在 trait JsonConverters
trait JsonConverter {
implicit val projectWrite: OWrites[Project] = Json.writes[Project]
implicit val projectRead: Reads[Project] = Json.reads[Project]
implicit lazy val taskWrite: OWrites[Task] = Json.writes[Task]
implicit lazy val taskRead: Reads[Task] = Json.reads[Task]
}
它工作正常,看起来也不错。但是我不明白为什么在单独的特征中没有惰性它就不能工作?谁能描述一下?或建议其他解决方案
普通变量(val
)和惰性变量(lazy val
)的初始化顺序不同。
val
按照文件中描述的顺序进行了初始化。
lazy val
在首次访问时 被初始化 。
在您的情况下,Project
包含一个 Task
,因此 projectRead
需要初始化 taskRead
才能完成。
在您的代码中,如果没有 lazy
,projectRead
在 taskRead
之前声明,因此当 projectRead
需要时 taskRead
仍然是 null
它。这就是你得到 NPE 的原因。
仍然,编译器可以找到隐式 taskRead
并且不会引发错误。
您对 lazy
的修复有效,因为 taskRead
现在在首次访问 时初始化 。现在,taskRead
在 projectRead
请求时初始化。 NPE 消失了。
您可以通过切换声明顺序来实现相同的修复:
trait JsonConverter {
implicit val taskWrite: OWrites[Task] = Json.writes[Task]
implicit val taskRead: Reads[Task] = Json.reads[Task]
implicit val projectWrite: OWrites[Project] = Json.writes[Project]
implicit val projectRead: Reads[Project] = Json.reads[Project]
}