Как сериализовать классы case с чертами с jsonspray
Я понимаю, что если у меня есть:
case class Person(name: String)
Я могу использовать
object PersonJsonImplicits extends DefaultJsonProtocol {
implicit val impPerson = jsonFormat1(Person)
}
и таким образом сериализуйте его с помощью:
import com.example.PersonJsonImplicits._
import spray.json._
new Person("somename").toJson
однако что, если у меня есть
trait Animal
case class Person(name: String) extends Animal
и у меня есть где-то в моем коде
val animal = ???
и мне нужно сериализовать его, и я хочу использовать json spray
какой сериализатор я должен добавить, я надеялся что-то вроде:
object AnimalJsonImplicits extends DefaultJsonProtocol {
implicit val impAnimal = jsonFormat???(Animal)
}
где, возможно, мне нужно было добавить некоторый матчи, чтобы проверить, какой тип является Animal, чтобы, если бы его лицо я направил его человеку, но ничего не нашел... читал https://github.com/spray/spray-json и не понимают, как это сделать.
так как я могу сериализовать набор
trait Animal
case class Person(name: String) extends Animal
с помощью json spray?
Ответы
Ответ 1
У вас есть несколько вариантов:
Вариант 1
Расширьте RootJsonFormat[Animal]
и поместите свою собственную логику для соответствия различным типам Animal
:
import spray.json._
import DefaultJsonProtocol._
trait Animal
case class Person(name: String, kind: String = "person") extends Animal
implicit val personFormat = jsonFormat2(Person.apply)
implicit object AnimalJsonFormat extends RootJsonFormat[Animal] {
def write(a: Animal) = a match {
case p: Person => p.toJson
}
def read(value: JsValue) =
// If you need to read, you will need something in the
// JSON that will tell you which subclass to use
value.asJsObject.fields("kind") match {
case JsString("person") => value.convertTo[Person]
}
}
val a: Animal = Person("Bob")
val j = a.toJson
val a2 = j.convertTo[Animal]
Если вы вставляете этот код в Scala REPL, вы получаете этот результат:
a: Animal = Person(Bob,person)
j: spray.json.JsValue = {"name":"Bob","kind":"person"}
a2: Animal = Person(Bob,person)
Источник
Вариант 2
Другой вариант - предоставить неявный jsonFormat
для Person
и любые другие подклассы Animal
, а затем записать код сериализации следующим образом:
def write(a: Animal) = a match {
case p: Person => p.toJson
case c: Cat => c.toJson
case d: Dog => d.toJson
}
Источник
Ответ 2
Это можно сделать с помощью расширения RootJsonFormat
. Примеры можно найти в здесь.
Ответ 3
Вы можете добавить дополнительное поле type
при сериализации и использовать его для определения реализации черты при десериализации:
trait Animal
case class Person(name: String) extends Animal
case class Lion(age: Int) extends Animal
implicit def personFormat = jsonFormat1(Person)
implicit def lionFormat = jsonFormat1(Lion)
implicit def animalFormat =
new RootJsonFormat[Animal] {
override def read(json: JsValue): Animal =
json.asJsObject.fields.get("type") match {
case Some(JsString("person")) => json.convertTo[Person]
case Some(JsString("lion")) => json.convertTo[Lion]
case t => deserializationError(s"Unable to deserialize Animal of type $t")
}
override def write(obj: Animal): JsValue =
obj match {
case e: Person => toJson(e, "person")
case e: Lion => toJson(e, "lion")
}
def toJson[T](obj: T, objType: String)(implicit w: JsonWriter[T]): JsObject = {
val o = obj.toJson.asJsObject
o.copy(fields = o.fields + ("type" -> JsString(objType)))
}
}