生成的播放json隐式实例导致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))
   }
}

运行时会导致StackOverflowError

如果我在lazy前面添加writesT,则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 lazy val writesT: OWrites[T] = Json.writes[T]

   def main(args: Array[String]): Unit = {
      val x = A("str")
      println(Json.toJson[T](x)(writesT))
   }
}

当我将StackOverflowError移到implicit函数中时,main也消失了:

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))
   }
}

有人可以向我解释为什么我在第一种情况下会得到StackOverflowError吗?

我怀疑它与初始化顺序以及play-json在后台使用的宏有关。但是如果是这样的话,我不明白为什么使用lazy会有所帮助,因为代码仍应在编译时生成,并且仅在以后的运行时对其进行评估不应更改任何内容。显然,在以后的情况下,writesA找到了writesT实例,但在第一种情况下却没有。为什么添加lazy可以解决隐式分辨率和宏代码生成的编译时问题?

这是一个完全不同的问题吗?

我正在使用Scala 2.12.3和play-json 2.6.2。

KINQWER123456789 回答:生成的播放json隐式实例导致StackOverflowError

这在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$3@69a03da1
x: A = A(str)

scala> println(Json.toJson[T](x)(writesT))
{"s":"str","_type":"A"}
本文链接:https://www.f2er.com/3145545.html

大家都在问