难以使用spray-json序列化immutable.Map

时间:2014-06-26 16:07:18

标签: scala spray-json

提前道歉交叉发布:我将此问题提交给喷涂用户列表,但列表似乎已经过审核,似乎没有人回家。希望SO是一个更好的场所。

我无法使用spray-json序列化复杂数据结构。例如,一个简单的immutable.Map[String,String]工作正常,但是immutable.Map[String,Foo],其中Foo是我定义的案例类。该文档给我的印象是,我只需要为JsonFormat定义一个Foo,我就准备好了。

以下是一些示例代码:

import spray.json._
import DefaultJsonProtocol._

case class Foo(hi: String)

object FooProtocol extends DefaultJsonProtocol {
  implicit val fooFormat: JsonFormat[Foo] = jsonFormat1(Foo)
}
import FooProtocol._

object Thing {
  def toSomething = {
    Map("foo" -> Foo("bar"), "baz" -> Foo("quux")).toJson
  }
}

会产生以下编译器错误:

[info] Compiling 1 Scala source to C:\spraytest\target\scala-2.10\classes...
[error] C:\spraytest\src\main\scala\Foo.scala:12: Cannot find JsonWriter or JsonFormat type class for scala.collection.immutable.Map[String,Foo]
[error]     Map("foo" -> Foo("bar"), "baz" -> Foo("quux")).toJson
[error]                                                    ^
[error] one error found
[error] (compile:compile) Compilation failed
[error] Total time: 5 s, completed Jun 26, 2014 11:40:26 AM

我很难过。

3 个答案:

答案 0 :(得分:4)

将您的FooProtocol重命名为Foo(这将使其成为伴随对象〜>免费的隐式范围),而是简单地继承import DefaultJsonProtocol._,例如:

import spray.json.DefaultJsonProtocol._
case class Foo(hi: String)
object Foo {
  implicit val fooJson = jsonFormat1(Foo.apply)
}

现在您需要使用您的Spray路线将正确的marshallers导入范围。 Marshaller for String可以在喷射DefaultJsonProtocol中找到,Foos marshaller可以从它的伴随对象中找到,所以你唯一需要导入的是spray.httpx.SprayJsonSupport,这应该可以解决问题。

或者你可以保留它而只是import FooProtocol。但从设计的角度来看,这不是最好的解决方案。

<强>更新

如果你看一下toJson签名,你会看到它请求一个隐含的JsonWriter,它是由jsonFormat方法生成的。 Companion对象解决了这个问题,因为scala编译器将其包含在隐式解析范围内,这是一种很好的做法,可以将您的隐含信息放在那里,因为这导致您不需要进行显式导入,例如{{1} }

对于Map和String marshallers,看看DefaultJsonProtocol trait,它扩展import FooProtocol._BasicFormats,它们分别有String和Map的编组器。您需要添加的唯一内容是CollectionFormats类的自己的编组器。

<强>解决方案

真正的问题在于范围内的多个marshaller含义。只需导入Foo即可访问所有标准含义(在本例中为Map和String)。当您从此特征扩展然后将其导入当前范围时,它还会导入所有标准编组。所以问题是你在范围内有多个marshallers导致scala编译器含糊不清的问题。

答案 1 :(得分:1)

AFAIK没有Map[String,_]的默认编组。您可以尝试显式转换对象toString(Map[String,String]可以序列化为Json)或为Map[String, Foo]提供编组器。

答案 2 :(得分:1)

您必须创建一个对象来保存地图,然后您可以序列化该对象。

import spray.json._
import DefaultJsonProtocol._

case class Foo(hi: String)

case class Bar( something: Map[String,Foo])

object FooBarProtocol extends DefaultJsonProtocol {
  implicit val fooFormat: JsonFormat[Foo] = jsonFormat1(Foo)
  implicit val barFormat: JsonFormat[Bar] = jsonFormat1(Bar)
}

object Thing {
  def toSomething = {
    Bar(Map("foo" -> Foo("bar"), "baz" -> Foo("quux"))).toJson
  }
}