play - 尝试实现 Writes to List[Any]

play - try to implements Writes to List[Any]

我有一个案例 class 看起来像这样:

case class A(名称:字符串,值:列表[任意]) 虽然值可以是长列表或字符串列表。

我正在尝试执行写入但没有成功。 这是我的代码:

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))

出于某种原因,当我尝试使用以下值编写案例 class 时:

A("name", List("val1", "val2"))

我得到以下异常:

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)

它在第一个 case 行失败:case longs: List[Long]

我不确定我做错了什么。有什么想法吗?

谢谢!

你很接近,但请记住,当你说 List[Any] 时,这意味着就类型系统而言,列表值可以是异构的,正如@m-z 指出的那样,泛型类型将被删除。

在这种情况下,我会使用 collectList[Any] 转换为 List[JsValue] 以丢弃任何非字符串或长整数的内容:

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))

我最近不得不自己做一些类似的事情来处理接受异构 JSON 数组的 Web 服务,但通常最好避免在 Scala 级别使用 Any

解决方案的另一个建议:

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))