你可以想象这样做:
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)