Playframeworks json写入隐式需要显式类型,为什么?

时间:2014-05-14 14:45:56

标签: json scala playframework playframework-2.2 implicit-conversion

这不编译:

package model

import play.api.libs.json._

case class Dog(id: Long, name: String, kind: String) {

  def asJson() = Json.toJson(this)
}

object Dog {
  implicit val writes = Json.writes[Dog]
}

错误:

53. Waiting for source changes... (press enter to interrupt)
[info] Compiling 1 Scala source to /Users/pablo/projects/mvp/target/scala-2.10/classes...
[error] /Users/pablo/projects/mvp/app/models/model2.scala:7: No Json deserializer found for type model.Dog. Try to implement an implicit Writes or Format for this type.
[error]   def asJson() = Json.toJson(this)
[error]                             ^
[error] one error found
[error] (compile:compile) Compilation failed

更改随播对象(请注意显式类型):

object Dog {
  implicit val writes: Writes[Dog] = Json.writes[Dog]
}

修复了这个问题。那是为什么?

3 个答案:

答案 0 :(得分:1)

这不是一个真正的答案(但我需要很多空间来粘贴我的代码!)但这对我有用:

package model

import play.api.libs.json._

object Dog {
  implicit val writes = Json.writes[Dog]
}

case class Dog(id: Long, name: String, kind: String) {

  def asJson() = Json.toJson(this)
}

基本上我只是在课前移动了对象声明。这可能是因为Json.writes使用宏,因此必须在使用构造的Write实例之前进行。

答案 1 :(得分:1)

宏是一个实验性功能,所以我不会过分依赖它。我知道这更冗长,但如果你使用组合器它应该工作:

implicit val writes: Writes[Dog] = (
  (JsPath \ "id").write[Long] and
  (JsPath \ "name").write[String] and
  (JsPath \ "kind").write[String]
)(unlift(Dog.unapply))

http://www.playframework.com/documentation/2.3.x/ScalaJsonCombinators

答案 2 :(得分:1)

Json.toJson是一个宏,在编译时执行。在那个阶段,尚未进行类型分析,这意味着无法自动推断方法参数(this)的类型(或者如果宏实现了执行此操作所需的额外功能,则可能会推断出它,但我认为这很难)。这就是你需要提供类型参数的原因,例如Json.toJson [Dog]。

声明伴侣对象时:

object Dog {
  implicit val writes: Writes[Dog] = Json.writes[Dog]
}

您为该类型声明了一个隐式编写器。给定Scala的隐式解析规则,当您尝试将case类实例转换为JSON时,将在作用域中找到writer。所以它可以工作,但不是因为类中的asJson()方法,而是因为隐式解析已经找到了在伴随对象中定义的编写器。