scalasprayspray-json

how to serialize case classes with traits with jsonspray


I understand that if I have:

case class Person(name: String)

I can use

object PersonJsonImplicits extends DefaultJsonProtocol {
  implicit val impPerson = jsonFormat1(Person)
}

and thus serialize it with:

import com.example.PersonJsonImplicits._
import spray.json._
new Person("somename").toJson

however what If i have

trait Animal
case class Person(name: String) extends Animal

and I have somewhere in my code

val animal = ???

and I need to serialize it and I want to use json spray

which serializer should I add I was hoping to have something like:

object AnimalJsonImplicits extends DefaultJsonProtocol {
  implicit val impAnimal = jsonFormat???(Animal)
}

where maybe I needed to add some matcher in order to check of what type is Animal so that if its a person I would direct it to person but found nothing... was reading https://github.com/spray/spray-json and don't understand how to do that..

so how can I serialize the set of

trait Animal
case class Person(name: String) extends Animal

with json spray?


Solution

  • You have a couple options:

    Option 1

    Extend RootJsonFormat[Animal] and put your custom logic for matching different types of 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]
    

    If you paste this code into the Scala REPL you get this output:

    a: Animal = Person(Bob,person)
    j: spray.json.JsValue = {"name":"Bob","kind":"person"}
    a2: Animal = Person(Bob,person)
    

    Source

    Option 2

    Another option is to supply implicit jsonFormats for Person and any other subclasses of Animal and then write your serialize code like so:

    def write(a: Animal) = a match {
      case p: Person => p.toJson
      case c: Cat => c.toJson
      case d: Dog => d.toJson
    }
    

    Source