spray-json:序列化一个通用特征

Posted

技术标签:

【中文标题】spray-json:序列化一个通用特征【英文标题】:spray-json: Serialize a generic trait 【发布时间】:2016-06-22 20:23:09 【问题描述】:

spray-json 依赖于在 T 的实例上调用 toJson 时存在的范围内隐式 JsonWriter[T]

假设我有几个具体子类型的特征,每个子类型都有一个 JsonWriter:

trait Base
case class Foo(a: Int) extends Base
case class Bar(a: Int, b: Int) extends Base
implicit val FooFormat = jsonFormat1(Foo)
implicit val BarFormat = jsonFormat2(Bar)

def go(o: Base) = 
    o.toJson

go 无法编译,因为 Base 没有 JsonWriter,即使所有具体子类型都有 编写器。

如何重新组织此代码,以便 Base 的通用函数使用适当的 json 格式化程序?

【问题讨论】:

将您的隐式包装在一个对象中并导入到go 的范围内。您可能仍然对 trait 本身有疑问,但除非您将其导入,否则 go 将无法使用这些隐式。 对不起,如果我的 sn-p 不清楚 - 我的问题是关于如何在 Base 上编写一个使用适当的具体隐式的函数,假设这些隐式的集合正确地在它需要的范围内成为。就目前而言,我知道如何写 go 如果它的参数是 (o: Foo),但不是 (o: Base) 【参考方案1】:

您可以使用具有类型和上下文边界的泛型方法。像这样:

def go[T <: Base : JsonWriter](t: T) = t.toJson

【讨论】:

很好。 : JsonWriter 本质上是否等同于 (implicit fmt: JsonWriter[T]) 没错! def f[T : X](t: T): T = t 将是 f: [T](t: T)(implicit evidence$1: X[T])T【参考方案2】:

编组Base 需要JsonFormat。以下是解决所述问题的方法之一。

import spray.json._

object BaseJsonProtocol extends DefaultJsonProtocol 
  implicit val FooFormat = jsonFormat1(Foo)
  implicit val BarFormat = jsonFormat2(Bar)

  implicit object BaseJsonFormat extends RootJsonFormat[Base] 
    def write(obj: Base) = obj match 
      case x: Foo ⇒ x.toJson
      case y: Bar ⇒ y.toJson
      case unknown @ _ => serializationError(s"Marshalling issue with $unknown")
    

    def read(value: JsValue) = 
      value.asJsObject.getFields("a","b") match 
        case Seq(JsNumber(a)) => Foo(a.toInt)
        case Seq(JsNumber(a), JsNumber(b)) => Bar(a.toInt, b.toInt)
        case unknown @ _ => deserializationError(s"Unmarshalling issue with $unknown ")
    
  
 

因此,BaseJsonProtocol 可用于编组Base 的实例,如下所示。

import BaseJsonProtocol._

def go(o: Base) =  o.toJson

assert (go(Foo(10)).convertTo[Foo] == Foo(10))
assert (go(Foo(10).asInstanceOf[Base]).convertTo[Foo] == Foo(10))
assert (go(Bar(10,100)).convertTo[Bar] == Bar(10, 100))

希望对您有所帮助!

【讨论】:

【参考方案3】:

我意识到一种解决方案是使go 成为具有类型绑定的泛型函数,并声明隐式值...“显式”

def go[T <: Base](t: T)(implicit fmt: JsonWriter[T]) =
    t.toJson

现在函数可以编译了,因为 JsonWriter 被承诺作为函数参数,并且每个调用站点都可以根据上下文(FooFormatBarFormat)拉入 JsonWriter[T] 的具体实现。

【讨论】:

如果Base 没有JsonFormat,例如:go(anyBase) 其中val anyBase: Base = Foo(1) 将无法编译。 是的,这是一个很好的观点——除了您对 BaseJsonFormat 的实现之外,是否还有另一种不需要枚举 Bar 子类型的 case 语句的方式来实现它?从概念上讲,似乎所​​有相关逻辑(具体的 JsonWriters + 类型层次结构)都已经定义了,让我感到困扰的是,我不得不在某个地方再次复制这些代码——无论是在 BaseJsonFormat 中还是在每种类型的单独 go 函数中.

以上是关于spray-json:序列化一个通用特征的主要内容,如果未能解决你的问题,请参考以下文章

Spray-json反序列化嵌套对象

Scala,spray-json:通用枚举 json 格式

Spray-json 用于 List 上的普通类(非大小写)

Scala's Play 中具有多个案例类(总和类型)的特征的 Json 序列化

使用喷雾 json 序列化 Map[String, Any]

当 json 元素不存在时,如何设置 spray-json 以设置 null?