Reputation: 2448
I have a case class that looks like this:
case class A(name: String, values: List[Any]) While values can be a list of long or string.
I am trying to implement a Writes without success. This the code I have:
implicit val myWrites: Writes[A] = (
(JsPath \ "name").write[String] and
(JsPath \ "values").write[JsArray].contramap[List[Any]](
(list: List[Any]) => list match{
case longs: List[Long] => JsArray(longs.map(l => JsNumber(l)))
case strings: List[String] => JsArray(strings.map(s => JsString(s)))
})
) (unlift(A.unapply))
From some reason, when I am trying to write the case class with the following values:
A("name", List("val1", "val2"))
I get the following exception:
java.lang.String cannot be cast to java.lang.Long
java.lang.ClassCastException: java.lang.String cannot be cast to
java.lang.Long
at scala.runtime.BoxesRunTime.unboxToLong(BoxesRunTime.java:105)
It fails on the first case line : case longs: List[Long]
I am not sure what I am doing wrong. any idea?
Thanks!
Upvotes: 2
Views: 88
Reputation: 2448
Another suggestion to the solution:
implicit val myWrites: Writes[A] = (
(JsPath \ "name").write[String] and
(JsPath \ "values").write[JsArray].contramap[List[Any]](
case l if l.isEmpty => JsArray(Seq())
case list@List(_: Long, _*) => JsArray(list.map(l => JsNumber(l.asInstanceOf[Long])))
case list@List(_: String, _*) => JsArray(list.map(s => JsString(s.asInstanceOf[String])))
) (unlift(A.unapply))
Upvotes: 0
Reputation: 8901
You're pretty close, but remember that when you say List[Any]
it means the list values can be heterogeneous as far as the type system is concerned, and as @m-z points out, the generic types will be erased.
In this situation I'd transform the List[Any]
into a List[JsValue]
using collect
to discard anything that's not either a string or a long:
case class A(name: String, values: List[Any])
implicit val myWrites: Writes[A] = (
(JsPath \ "name").write[String] and
(JsPath \ "values").write[List[JsValue]].contramap[List[Any]](
_.collect {
case str: String => JsString(str)
case long: Long => JsNumber(long)
})
)(unlift(A.unapply))
I've had to do something similar myself recently for dealing with a web service that accepted heterogeneous JSON arrays, but it's usually a good idea to avoid using Any
at the Scala level.
Upvotes: 2