how to serialize case classes with traits with jsonspray

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