代码之家  ›  专栏  ›  技术社区  ›  acjay Sreekanth

Seq of Eithers的spray json失败

  •  7
  • acjay Sreekanth  · 技术社区  · 10 年前

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

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

    因此,所有构建块似乎都有效,但 Seq Either 失败了,即使我试着用勺子喂它。

    我看到以下错误:

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

    知道是什么吗?

    2 回复  |  直到 10 年前
        1
  •  14
  •   Travis Brown    10 年前

    这是最烦人的事情之一 Either 这个 Left Right 构造函数都会扩展 Product Serializable 但是 任何一个 它本身不会,这导致了可怕的推断类型:

    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] ,但推断类型中的额外垃圾意味着编译器找不到它。

    如果你没有 正确的 按顺序:

    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,""]
    

    在很多情况下,这不是问题,因为你经常会 任何一个 显式返回 任何一个 而不是使用 左边 正确的 直接导致这个问题。

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

        2
  •  2
  •   Steve Buzzard    10 年前

    我想如果你给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标准库已将extends Product with Serializable添加到抽象类Ethernet定义中,则无需执行此操作,但由于Right和Left子类型都是case类(它们隐式扩展了Product和Serializaable),因此它们被包含在推断类型中,这会导致spray所需的不变量类型出现问题。