2

我希望能够使用 spray-json 在 Scala 中创建案例类,但asJson在类中定义一个方法,但我似乎无法弄清楚如何。例如,我希望能够做到这一点:

case class Foo(bar: String) {
  def toJson: JsValue = ...
}

创建一个隐式 JSON 转换器很容易:

object JsonProtocols extends DefaultJsonProtocol {
  implicit val fooFormat = jsonFormat1(Foo)
}

但据我所知,这只能在课外进行。我很想找到一种声明 JSON 格式在类本身中转换为 JSON 的方法。

4

1 回答 1

3

你可以想象这样做:

scala> import spray.json._
import spray.json._

scala> case class Foo(bar: String) {
  def toJson:JsValue = JsObject( "bar" -> JsString(bar) )
}
defined class Foo

scala> Foo("bar").toJson
res2: spray.json.JsValue = {"bar":"bar"}

到目前为止一切顺利,但这不适合 Spray 的类型类机制。例如,Spray 的路由 DSL 会在您尝试将 Foo 转换为 JsValue 或从 JsValue 转换(例如使用 route entity( as[Foo] ) { ... })时给您一个类型错误。对于 List 和 Set 等类型,他们已经为您准备的隐式也不能与 Foo 一起使用:

scala> import DefaultJsonProtocol._
import DefaultJsonProtocol._

scala> List(Foo("bar")).toJson
<console>:31: error: Cannot find JsonWriter or JsonFormat type class for List[Foo]
              List(Foo("bar")).toJson

因为没有 JsonFormat 类供他们用来转换 Foo,就像JsonFormat1(Foo)创建的那样。

然后您可能会考虑将格式放入 Foo 伴随对象中,因为范围内类的伴随对象位于隐式搜索路径上,如下所示:

object Foo extends DefaultJsonProtocol {
  implicit val fooFormat = jsonFormat1(Foo)
}
case class Foo(bar: String)

但是因为此时我们还没有完成对 Foo 的定义,所以编译器会给我们一个类型错误:

[error]  found   : Foo.type
[error]  required: ? => ?
[error]  Note: implicit value fooFormat is not applicable here because it comes after the application point and it lacks an explicit result type

添加显式结果类型RootJsonFormat[Foo]并不能解决问题:

[error]  found   : Foo.type
[error]  required: ? => Foo
[error]   implicit val fooFormat:RootJsonFormat[Foo] = jsonFormat1(Foo)

诀窍(感谢 knutwalker!)是明确传递Foo.apply

object Foo extends DefaultJsonProtocol {
  implicit val fooFormat = jsonFormat1(Foo.apply)
}
case class Foo(bar: String)
于 2014-04-09T06:39:59.050 回答