Спрей Json: получить Json String из общих объектов - PullRequest
0 голосов
/ 14 сентября 2018

У меня есть следующие классы

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: предупреждение: абстрактный тип K в шаблоне типа Foo [K, V] не проверяется, поскольку он устраняется в случае стирания e: Foo [K, V] => jsonFormat2 (Foo.apply [K,V].нельзя использовать контекстную привязку в match-case в write методе baseJsonConvertor.

Может кто-нибудь помочь обойти эту проблему?

Ответы [ 2 ]

0 голосов
/ 14 сентября 2018

Как правило, вы не можете создать JsonFormat для черты с универсальными дочерними классами (особенно если у них есть разные наборы общих параметров).

Например, эту черту нельзя сериализовать с помощью spray-json:

sealed trait Base
case class Foo[K, V](key: K, value: V) extends Base
case class Bar[K, V, T](key: V, value: K, param: T) extends Base 

Я думаю, вы должны заключить ваши универсальные классы в элемент, который знает все универсальные параметры.

Как Either в этом примере:

import spray.json.DefaultJsonProtocol._
import spray.json._
case class Foo[K, V](key: K, value: V)
case class Bar[K, V](key: V, value: K)
implicit def fooJsonFormat[K: JsonFormat, V: JsonFormat] = jsonFormat2(Foo.apply[K, V])
implicit def barJsonFormat[K: JsonFormat, V: JsonFormat] = jsonFormat2(Bar.apply[V, K])
val list = List[Either[Foo[String, Int], Bar[Int, String]]](Left(Foo("One", 1)), Right(Bar(2, "Two")))
println(list.toJson)
0 голосов
/ 14 сентября 2018

Этот пример будет работать в данном конкретном случае:

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, чтобы быть видимым в Spec.

...