生成的播放 json 隐式实例导致 StackOverflowError
Generated play json implicit instances cause StackOverflowError
我有以下代码:
import play.api.libs.json._
object Test {
sealed trait T
case class A(s: String) extends T
implicit val writesA: OWrites[A] = Json.writes[A]
implicit val writesT: OWrites[T] = Json.writes[T]
def main(args: Array[String]): Unit = {
val x = A("str")
println(Json.toJson[T](x)(writesT))
}
}
当 运行 时会导致 WhosebugError
。
如果我在 writesT
前面添加 lazy
,WhosebugError
就会消失,一切正常:
import play.api.libs.json._
object Test {
sealed trait T
case class A(s: String) extends T
implicit val writesA: OWrites[A] = Json.writes[A]
implicit lazy val writesT: OWrites[T] = Json.writes[T]
def main(args: Array[String]): Unit = {
val x = A("str")
println(Json.toJson[T](x)(writesT))
}
}
当我将 implicit
移入 main
函数时,WhosebugError
也会消失:
import play.api.libs.json._
object Test {
sealed trait T
case class A(s: String) extends T
def main(args: Array[String]): Unit = {
implicit val writesA: OWrites[A] = Json.writes[A]
implicit val writesT: OWrites[T] = Json.writes[T]
val x = A("str")
println(Json.toJson[T](x)(writesT))
}
}
谁能给我解释一下为什么我在第一种情况下会得到 WhosebugError
?
我怀疑它与初始化顺序和 play-json 在后台使用的宏有关。但如果是这种情况,我不明白为什么使用 lazy
会有所帮助,因为代码仍应在编译时生成,并且稍后在 运行 时简单地对其进行评估不应该改变任何东西。显然在后面的情况下 writesA
实例被 writesT
找到,但在第一种情况下没有。为什么添加 lazy
可以解决隐含解析和宏代码生成的编译时问题?
或者这是一个完全不同层面的问题?
我正在使用 Scala 2.12.3 和 play-json 2.6.2.
这与 Play JSON 2.7.x
配合得很好
import play.api.libs.json._
sealed trait T
case class A(s: String) extends T
implicit val writesA: OWrites[A] = Json.writes[A]
implicit val writesT: OWrites[T] = Json.writes[T]
val x = A("str")
println(Json.toJson[T](x)(writesT))
// Exiting paste mode, now interpreting.
scala> println(Json.toJson[T](x)(writesT))
^
{"s":"str","_type":"$line2.$read.$iw.$iw.A"}
猜想这与 "blight of contravariant" on Writes
implicit 之后的修复有关。
Play JSON 2.6 的解决方法是手动实现密封特性的 Writes
(或 OFormat
)实例。
scala> :paste
// Entering paste mode (ctrl-D to finish)
import play.api.libs.json._
sealed trait T
case class A(s: String) extends T
implicit val writesT: OWrites[T] = {
val writesA: OWrites[A] = Json.writes[A]
OWrites[T] {
case t: A => writesA.writes(t) + ("_type" -> Json.toJson("A"))
case _ => ???
}
}
val x = A("str")
// Exiting paste mode, now interpreting.
import play.api.libs.json._
defined trait T
defined class A
writesT: play.api.libs.json.OWrites[T] = play.api.libs.json.OWrites$$anon@69a03da1
x: A = A(str)
scala> println(Json.toJson[T](x)(writesT))
{"s":"str","_type":"A"}
我有以下代码:
import play.api.libs.json._
object Test {
sealed trait T
case class A(s: String) extends T
implicit val writesA: OWrites[A] = Json.writes[A]
implicit val writesT: OWrites[T] = Json.writes[T]
def main(args: Array[String]): Unit = {
val x = A("str")
println(Json.toJson[T](x)(writesT))
}
}
当 运行 时会导致 WhosebugError
。
如果我在 writesT
前面添加 lazy
,WhosebugError
就会消失,一切正常:
import play.api.libs.json._
object Test {
sealed trait T
case class A(s: String) extends T
implicit val writesA: OWrites[A] = Json.writes[A]
implicit lazy val writesT: OWrites[T] = Json.writes[T]
def main(args: Array[String]): Unit = {
val x = A("str")
println(Json.toJson[T](x)(writesT))
}
}
当我将 implicit
移入 main
函数时,WhosebugError
也会消失:
import play.api.libs.json._
object Test {
sealed trait T
case class A(s: String) extends T
def main(args: Array[String]): Unit = {
implicit val writesA: OWrites[A] = Json.writes[A]
implicit val writesT: OWrites[T] = Json.writes[T]
val x = A("str")
println(Json.toJson[T](x)(writesT))
}
}
谁能给我解释一下为什么我在第一种情况下会得到 WhosebugError
?
我怀疑它与初始化顺序和 play-json 在后台使用的宏有关。但如果是这种情况,我不明白为什么使用 lazy
会有所帮助,因为代码仍应在编译时生成,并且稍后在 运行 时简单地对其进行评估不应该改变任何东西。显然在后面的情况下 writesA
实例被 writesT
找到,但在第一种情况下没有。为什么添加 lazy
可以解决隐含解析和宏代码生成的编译时问题?
或者这是一个完全不同层面的问题?
我正在使用 Scala 2.12.3 和 play-json 2.6.2.
这与 Play JSON 2.7.x
配合得很好import play.api.libs.json._
sealed trait T
case class A(s: String) extends T
implicit val writesA: OWrites[A] = Json.writes[A]
implicit val writesT: OWrites[T] = Json.writes[T]
val x = A("str")
println(Json.toJson[T](x)(writesT))
// Exiting paste mode, now interpreting.
scala> println(Json.toJson[T](x)(writesT))
^
{"s":"str","_type":"$line2.$read.$iw.$iw.A"}
猜想这与 "blight of contravariant" on Writes
implicit 之后的修复有关。
Play JSON 2.6 的解决方法是手动实现密封特性的 Writes
(或 OFormat
)实例。
scala> :paste
// Entering paste mode (ctrl-D to finish)
import play.api.libs.json._
sealed trait T
case class A(s: String) extends T
implicit val writesT: OWrites[T] = {
val writesA: OWrites[A] = Json.writes[A]
OWrites[T] {
case t: A => writesA.writes(t) + ("_type" -> Json.toJson("A"))
case _ => ???
}
}
val x = A("str")
// Exiting paste mode, now interpreting.
import play.api.libs.json._
defined trait T
defined class A
writesT: play.api.libs.json.OWrites[T] = play.api.libs.json.OWrites$$anon@69a03da1
x: A = A(str)
scala> println(Json.toJson[T](x)(writesT))
{"s":"str","_type":"A"}