对于具有通用类型属性的case类,play-json格式宏失败

时间:2014-11-20 11:20:39

标签: json scala playframework playframework-2.3

我无法弄清楚为什么无法使用play-json的Json.format[]宏处理以下大小写类构造。更具体地说,编译器引发的错误并没有给我任何关于问题是什么以及如何解决它的线索。

鉴于以下定义:

trait Evt[T <: Evt[T]] {
  def meta: Meta[T]
}
case class Meta[T <: Evt[T]](value: String)
case class X(meta: Meta[X], data: String) extends Evt[X]

我尝试实现如下的JSON格式。 Meta[X]的格式只是初步尝试。我更关注使用Json.format[X]的标准方法和下面描述的编译错误。

import play.api.libs.json._

object Formats {
  implicit val metaFormat = new Format[Meta[X]] {
    override def writes(o: Meta[X]): JsValue = JsString(o.value)
    override def reads(json: JsValue): JsResult[Meta[X]] = json.validate[String] match {
      case JsSuccess(value, path) => JsSuccess(Meta[X](value), path)
      case error: JsError => error
    }
  }
  implicit val xFormat: Format[X] = Json.format[X]
}

尝试编译它会产生以下错误:

[error] ...: type mismatch;
[error]  found   : X
[error]  required: Meta[X]
[error]   implicit val xFormat: Format[X] = Json.format[X]
[error]                                                ^

我想了解为什么会发生这种情况,以及可能解决方法的一些线索,以便我可以像上面那样格式化构造。

使用

  • Scala 2.11.4
  • 播放json_2.11-2.3.6

更新

经过一些思考和挖掘后,我怀疑编译器宏生成的代码会导致此编译错误,因为至少对我来说错误消息对于打印的代码行没有任何意义。我试图用scalac选项-Ymacro-debug-lite检查代码,这就是我得到的:

{
  import play.api.libs.functional.syntax._;
  final class $anon extends play.api.libs.json.util.LazyHelper[Format, X] {
    def <init>() = {
      super.<init>();
      ()
    };
    override lazy val lazyStuff: Format[X] = play.api.libs.json.JsPath.$bslash("meta").lazyFormat(this.lazyStuff).and(play.api.libs.json.JsPath.$bslash("data").format(json.this.Format.GenericFormat[String](json.this.Reads.StringReads, json.this.Writes.StringWrites))).apply(((meta, data) => X.apply(meta, data)), play.api.libs.functional.syntax.unlift(X.unapply))
  };
  new $anon()
}.lazyStuff

我认为部分$bslash("meta").lazyFormat(this.lazyStuff)可能会导致编译错误,因为this.lazyStuff会返回Format[X]而在此地点(路径为meta)a {{1}将是必需的。

通过此分析,它看起来像是Format[Meta[X]]宏的缺点。但是,我不确定这种分析是否正确,所以仍然希望得到一些澄清。

更新2

更多实验表明,此问题不仅限于递归定义的类型。同时尝试对此构造使用format宏也会失败并出现相同的错误:

format

我在宏here上找到了一些可能相关的工作。但是使用2.4.0-M1的播放框架仍然会发现同样的问题。

1 个答案:

答案 0 :(得分:0)

Json.format[A]是一个用于序列化简单案例类的宏。我相信它在文档中的某处警告不要使用递归定义的类型。它似乎使用json组合器:

import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val XFormat: Format[X] = (
    (__ \ "meta").format[Meta[X]] and 
    (__ \ "data").format[String]
)(X.apply _, unlift(X.unapply))


scala> val x = X(Meta[X]("meta"), "data")
x: X = X(Meta(meta),data)

scala> Json.toJson(x)
res1: play.api.libs.json.JsValue = {"meta":"meta","data":"data"}