【问题标题】:spray-json failing for Seq of EithersSpray-json 的序列失败
【发布时间】:2016-07-22 13:15:45
【问题描述】:

不确定这是一个错误,但以下演示在最终情况下失败:

import spray.json._
import DefaultJsonProtocol._

object SprayTest {
  1.toJson
  "".toJson
  (Left(1): Either[Int, String]).toJson
  (Right(""): Either[Int, String]).toJson
  Seq(1).toJson
  Seq("").toJson
  Seq(Left(1), Right("")).toJson
  Seq(Left(1), Right("")).toJson(seqFormat(eitherFormat(IntJsonFormat, StringJsonFormat)))
}

所以所有构建块似乎都可以工作,但 SeqEither 的格式组合失败,即使我尝试用勺子喂它。

我看到以下错误:

[error] SprayTest.scala:11: Cannot find JsonWriter or JsonFormat type class for Seq[Product with Serializable with scala.util.Either[Int,String]]
[error]   Seq(Left(1), Right("")).toJson
[error]                           ^
[error] SprayTest.scala:12: type mismatch;
[error]  found   : spray.json.DefaultJsonProtocol.JF[Either[Int,String]]
[error]     (which expands to)  spray.json.JsonFormat[Either[Int,String]]
[error]  required: spray.json.JsonFormat[Product with Serializable with scala.util.Either[Int,String]]
[error] Note: Either[Int,String] >: Product with Serializable with scala.util.Either[Int,String] (and spray.json.DefaultJsonProtocol.JF[Either[Int,String]] <: spray.json.JsonFormat[Either[Int,String]]), but trait JsonFormat is invariant in type T.
[error] You may wish to define T as -T instead. (SLS 4.5)
[error]   Seq(Left(1), Right("")).toJson(seqFormat(eitherFormat(IntJsonFormat, StringJsonFormat)))

知道什么给了?

【问题讨论】:

    标签: json scala covariance spray-json


    【解决方案1】:

    我认为,如果您向 Seqs 的元素添加类型归属,它将编译:

    Seq(Left(1): Either[Int, String], Right(""): Either[Int, String]).toJson
    Seq(Left(1): Either[Int, String], Right(""): Either[Int, String]).toJson(seqFormat(eitherFormat(IntJsonFormat, StringJsonFormat))
    

    你也可以给它一个单一的类型归属:

    (Seq(Left(1), Right("")): Either[Int, String]).toJson
    (Seq(Left(1), Right("")): Either[Int, String]).toJson(seqFormat(eitherFormat(IntJsonFormat, StringJsonFormat))
    

    我认为问题在于 scalac 试图确定您提供给 Seq 的元素之间的共同最小上限以派生单一类型(因为标准集合需要其元素的同质数据类型)并且它不推断什么你想要它而不给它帮助。如果 scala 标准库已将具有 Serializable 的扩展产品添加到抽象类 Either 定义中,则不需要这样做,但由于子类型 Right 和 Left 都是案例类(隐式扩展 Product 和 Serializable),它们会被包含在内在推断的类型中,这会导致您出现喷雾所需的不变类型的问题。

    【讨论】:

    • 看起来 Travis 打败了我。 :-)
    【解决方案2】:

    这是关于 Either 的最烦人的事情之一——LeftRight 构造函数都扩展了 ProductSerializable,但 Either 本身没有,这导致了糟糕的推断类型:

    scala> Seq(Left(1), Right(""))
    res0: Seq[Product with Serializable with scala.util.Either[Int,String]] = List(Left(1), Right())
    

    因为JsonFormat 在其类型参数中是不变的,所以您拥有A 的实例并不意味着您拥有Product with Serializable with A 的实例。具体在您的情况下,实际上有一个Either[Int, String] 的实例,但是推断类型中的额外垃圾意味着编译器找不到它。

    如果序列中没有Right,也会发生类似的情况:

    scala> Seq(Left(1), Left(2)).toJson
    <console>:18: error: Cannot find JsonWriter or JsonFormat type class for Seq[scala.util.Left[Int,Nothing]]
           Seq(Left(1), Left(2)).toJson
                                 ^
    

    您可以通过提供类型而不是使用推断的类型来解决这两个问题:

    scala> val xs: Seq[Either[Int, String]] = Seq(Left(1), Right(""))
    xs: Seq[Either[Int,String]] = List(Left(1), Right())
    
    scala> xs.toJson
    res1: spray.json.JsValue = [1,""]
    

    在许多情况下,这不是问题,因为您通常会从显式返回 Either 的方法中获取 Either 值,而不是直接使用 LeftRight 导致这种情况问题。

    作为脚注:这就是为什么在定义自己的 ADT 时应该始终让根密封特征(或密封类)扩展 Product with Serializable。如果标准库设计者遵循了这个建议,我们都会变得更好。

    【讨论】:

    • 这修复了我的示例,但不幸的是,它没有修复我的真实代码 :( 但非常有用,谢谢!
    猜你喜欢
    • 2020-06-30
    • 1970-01-01
    • 1970-01-01
    • 2014-03-08
    • 1970-01-01
    • 2019-03-08
    • 2017-11-10
    • 1970-01-01
    相关资源
    最近更新 更多