I am trying to write a JsonFormat for an abstract class with a generic parameter that looks like something like this:
abstract class Animal[A] {
def data: A
def otherStuff: String = "stuff"
}
case class CatData(catField: String)
case class Cat(data: CatData) extends Animal[CatData]
So far my attempt at this looks like:
object AnimalProtocol extends DefaultJsonProtocol {
implicit val catDataFormat = jsonFormat1(CatData)
implicit val catFormat = jsonFormat1(Cat)
implicit def animalFormat[T <: Animal[T]](t: T)(implicit fmt: JsonWriter[T]) = new RootJsonFormat[Animal[T]] {
def write(obj: Animal[T]) = obj match {
case x: Cat => catFormat.write(x)
}
def read(json: JsValue) = ???
}
Now, if I try to do this:
import AnimalProtocol._
val cat: Animal[CatData] = Cat(CatData("this is cat data"))
I get the compiler error:
Cannot find JsonWriter or JsonFormat type class for Animal[CatData]
How can I make it work? In the end I want to write json with the fields in Animal
and with data
set to whatever case class applies.
You need to provide a type parameter for both the generic field and the subclass of Animal in your
implicit def
:That also allows you to get rid of pattern matching on subclasses inside
animalFormat
.I don't use spray-json (I've got much more experience with play-json), but I'll try to help by pointing at a few strange things in your code.
I'm not sure you need
implicit val catFormat = jsonFormat1(Cat)
, unless you want it to be applied instead ofanimalFormat
when type is known to beCat
.You definition of
animalFormat
looks wrong/strange for the following reasons:T <: Animal[T]
doesn't correspond to your types i.e., you don't haveCatData <: Animal[CatData]
t
fmt
(but instead you pattern match onobj
)I would suggest to either define a static
animalFormat
, something like (not sure about the wildcard type_
):or, without using pattern matching:
Note that with this approach, you won't be able to read a generic
Animal
as there's no type information in the json.