简体   繁体   English

spray-json因为Eithers的Seq而失败了

[英]spray-json failing for Seq of Eithers

Not sure this is a bug, but the following demo fails on the final cases: 不确定这是一个错误,但以下演示在最终案例中失败:

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

So all the building blocks appear to work, but the composition of the format for Seq and Either fails, even if I try to spoon-feed it. 因此所有构建块似乎都有效,但是SeqEither格式的组合都失败了,即使我尝试用勺子喂它。

I see the following errors: 我看到以下错误:

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

Any idea what gives? 知道是什么给出的吗?

This is one of the most annoying things about Either —the Left and Right constructors both extend Product and Serializable , but Either itself doesn't, which leads to awful inferred types: 这是关于最讨厌的事情之一Either -the LeftRight的构造都延伸ProductSerializable ,但Either本身并没有,这会导致可怕的推断类型:

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

Because JsonFormat is invariant in its type parameter, the fact that you have an instance for A doesn't mean you have an instance for Product with Serializable with A . 因为JsonFormat在其类型参数中是不变的,所以您拥有A实例并不意味着您有一个Product with Serializable with A的实例。 In your case specifically, there is actually an instance for Either[Int, String] , but the extra garbage in the inferred type means the compiler can't find it. 在具体情况下,实际上有一个Either[Int, String]的实例,但推断类型中的额外垃圾意味着编译器无法找到它。

A similar thing happens if you don't have a Right in the sequence: 如果序列中没有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
                             ^

You can fix both problems by providing a type instead of using the inferred one: 您可以通过提供类型而不是使用推断的类型来解决这两个问题:

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

In many cases this isn't an issue, since you'll often get your Either values from methods that explicitly return an Either instead of using Left and Right directly in ways that lead to this problem. 在许多情况下,这不是问题,因为您经常会从显式返回Either方法中获取您的Either值,而不是直接以导致此问题的方式使用LeftRight

As a footnote: this is why you should always have your root sealed trait (or sealed class) extend Product with Serializable when you're defining your own ADTs. 作为脚注:这就是为什么在定义自己的ADT时,应始终使用root密封特性(或密封类)扩展Product with Serializable We'd all be a lot better off if the standard library designers had followed that advice. 如果标准图书馆设计师遵循这一建议,我们会好得多。

I think if you add type ascriptions to the elements of the Seqs It'll compile: 我想如果你将类型归属添加到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))

you can also give it a single type ascription: 你也可以给它一个类型的归属:

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

I think the problem is scalac's trying to determine the common least upper bounds between the elements you're providing to Seq to derive a single type (since standard collections require homogeneous data types for their elements) and it doesn't infer what you want it to without giving it help. 我认为问题是scalac试图确定你为Seq提供的元素之间的共同最小上界来导出单一类型(因为标准集合需要为其元素提供同类数据类型)并且它不会推断出你想要它的内容没有给予帮助。 If the scala standard library had added extends Product with Serializable to the abstract class Either definition you wouldn't need to do this, but since both the subtypes Right and Left are case classes (which implicitly do extend Product and Serializable), they get included in the inferred type, which is causing you issues with the invariant type required by spray. 如果scala标准库已将扩展Product with Serializable添加到抽象类中,则无需执行此任何定义,但由于子类型Right和Left都是case类(隐式地扩展Product和Serializable),因此它们包括在内在推断类型中,这会导致喷涂所需的不变类型出现问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM