首页 > 解决方案 > Spray Json:从通用对象中获取 Json 字符串

问题描述

我有以下课程

import spray.json._
sealed trait Base
case class Foo[K, V](key : K, value : V) extends Base
case class Bar[K, V](key : V, value : K) extends Base 

他们对应的 json 转换器

implicit def baseJsonConvertor[K: JsonFormat, V: JsonFormat] = new JsonFormat[Base] {
  override def read(json: JsValue): Base =
    throw new SerializationException("Don't use this for reading...")

  override def write(obj: Base): JsValue = obj match {
    case e : Foo[K, V] => jsonFormat2(Foo.apply[K, V]).toJson
    case e : Bar[K, V] => jsonFormat2(Bar.apply[V, K]).toJson
  }
}
implicit def fooJsonConvertor[K: JsonFormat, V: JsonFormat] = jsonFormat2(Foo.apply[K, V])
implicit def barJsonConvertor[K: JsonFormat, V: JsonFormat] = jsonFormat2(Bar.apply[V, K])

当我尝试这个

val list = List(Foo[String, Int]("One", 1), Bar(2, "Two") ).map(_.toJson)

我得到这个错误

:216: 警告:类型模式 Foo[K,V] 中的抽象类型 K 未选中,因为它被擦除案例 e 消除:Foo[K, V] => jsonFormat2(Foo.apply[K, V]).toJson

使用 Base val list = List(FooString, Int).map(_.toJson) 找不到具有 Serializable 的 Product 的 JsonWriter 或 JsonFormat 类型类

我不能match-casewrite.baseJsonConvertor

有人可以帮助解决这个问题吗?

标签: scalaspray-json

解决方案


此示例将适用于这种特殊情况:

import org.scalatest.{Matchers, WordSpecLike}



object O {

  import spray.json._
  import spray.json.DefaultJsonProtocol._

  sealed trait Base[K, V]

  case class Foo[K, V](key: K, value: V) extends Base[K, V]

  case class Bar[K, V](key: V, value: K) extends Base[K, V]

  implicit def fooJsonConvertor[K: JsonFormat, V: JsonFormat] = jsonFormat2(Foo.apply[K, V])

  implicit def barJsonConvertor[K: JsonFormat, V: JsonFormat] = jsonFormat2(Bar.apply[V, K])



  implicit def baseJsonConvertor[K: JsonFormat: Manifest, V: JsonFormat: Manifest] = new JsonFormat[Base[K, V]] {
    override def read(json: JsValue): Base[K, V] =
      throw new SerializationException("Don't use this for reading...")

    override def write(obj: Base[K, V]): JsValue = obj match {
      case e@Foo(_: K, _: V) => implicitly[JsonFormat[Foo[K,V]]].write(e.asInstanceOf[Foo[K, V]])

      case e@Bar(_: V, _: K) => implicitly[JsonFormat[Bar[K,V]]].write(e.asInstanceOf[Bar[K, V]])
    }
  }




}

class Spec extends WordSpecLike with Matchers {

  import O._
  import spray.json._
  import spray.json.DefaultJsonProtocol._
  "test" should {

    "test" in {

      val list = List[Base[String, Int]](Foo[String, Int]("One", 1), Bar(2, "Two")).map(_.toJson)

      println(list)


    }



  }


}

但是,您不能使用没有类型参数的 Base,因为没有它们您将无法派生 JsonFormat。请注意,您必须以它们使用的相反顺序声明非法 jsonFormats。我的意思是您必须首先为 JsonFormat Foo 和 Bar 声明隐式,然后再为 Base 声明。否则他们将无法被发现。您还应该使用隐式 Manifest 才能安全匹配。

如果您实际上遇到了更复杂的情况,子类中的类型参数数量可变,那么这对您不起作用。我认为在不消除子类中的类型参数的情况下,参数数量可变的情况无法有效解决。另请注意,如果对象 O 与 Spec 在同一个文件中,则必须在 Spec 之前声明包含隐式的对象 O 才能在 Spec 中可见。


推荐阅读