I have a custom class as follows
object SafeList {
def apply[A](x: List[A]): SafeList[A] = if (x == null) EmptyList else HasItems[A](x)
}
sealed abstract class SafeList[+A] extends Product with Serializable {
def get: List[A]
}
final case class HasItems[+A](x: List[A]) extends SafeList[A] {
def get = x
}
case object EmptyList extends SafeList[Nothing] {
def get = Nil
}
And a formatter for the SafeList which looks like this
...
import spray.json.DefaultJsonProtocol._
trait SafeCollectionFormats {
implicit def safeListFormat[A: RootJsonFormat] = new RootJsonFormat[SafeList[A]] {
def read(json: JsValue): SafeList[A] = {
val list: List[A] = listFormat[A].read(json)
SafeList[A](list)
}
def write(sl: SafeList[A]): JsValue =
listFormat[A].write(sl.get)
}
}
object SafeCollectionFormats extends SafeCollectionFormats
And that compiles.
But when I add a test for my formatter, like so....
...
import spray.json.DefaultJsonProtocol._
import marshalling.SafeCollectionFormats._
...
"Unmarshalling a json array with items" should "produce a SafeList with items" in {
val json: JsValue = JsArray(JsString("a"), JsString("b"), JsString("c"))
val list = List("a", "b", "c")
val result = json.convertTo[SafeList[String]]
assertResult(list)(result)
}
...
I get the following compilation error
Error:(14, 32) Cannot find JsonReader or JsonFormat type class for myapp.types.SafeList[String]
val result = json.convertTo[SafeList[String]]
^
I think there may be something in this answer to help me but it's a bit advanced for me. I thought my implicit safeListFormat was the JsonReader for my SafeList and i'm importing it into my Spec. I dont know if the parameterised types are confusing things?
Any ideas what i'm doing wrong?
Edit: Whilst my test at the moment is creating a SafeList of Strings, the ultimate intention is to create a SafeList of my domain objects. I will need to add a second test that builds a JsArray of MyObjects. And so the type of A - in JSON terms will be different. My SafeList needs to cope with both simple objects like Strings, and domain objects. I think I might raise this as a second SO question but I mention it here for context
It works for me with just one little change: I made SafeCollectionFormats
extend DefaultJsonProtocol
.
I also had to change the context bound for your safeListFormat
to [A: JsonFormat]
.
trait SafeCollectionFormats extends DefaultJsonProtocol {
implicit def safeListFormat[A: JsonFormat] = new RootJsonFormat[SafeList[A]] {
def read(json: JsValue): SafeList[A] = {
val list: List[A] = listFormat[A].read(json)
SafeList[A](list)
}
def write(sl: SafeList[A]): JsValue =
listFormat[A].write(sl.get)
}
}
object SafeCollectionFormats extends SafeCollectionFormats
Hope this helps you.