Gio*_*tti 7 json scala shapeless circe
我可以很容易地为一个密封的案例类家族推导出一个编解码器,如下所示:
import io.circe._
import io.circe.generic.auto._
sealed trait Base
case class X(x: Int) extends Base
case class Y(y: Int) extends Base
object Test extends App {
val encoded = Encoder[Base].apply(Y(1))
val decoded = Decoder[Base].apply(encoded.hcursor)
println(decoded) // Right(Y(1))
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我将类型成员添加到基类中,我就不能再这样做了,即使它受到密封特性的限制:
import io.circe._
import io.circe.generic.auto._
sealed trait Inner
case class I1(i: Int) extends Inner
case class I2(s: String) extends Inner
sealed trait Base { type T <: Inner }
case class X[S <: Inner](x: S) extends Base { final type T = S }
case class Y[S <: Inner](y: S) extends Base { final type T = S }
object Test extends App {
val encodedInner = Encoder[Inner].apply(I1(1))
val decodedInner = Decoder[Inner].apply(encodedInner.hcursor) // Ok
println(decodedInner) // Right(I1(1))
// Doesn't work: could not find implicit for Encoder[Base] etc
// val encoded = Encoder[Base].apply(Y(I1(1)))
// val decoded = Decoder[Base].apply(encoded.hcursor)
// println(decoded)
}
Run Code Online (Sandbox Code Playgroud)
有没有办法可以达到我的目的?如果没有,我可以改变什么来获得类似的东西?
这不起作用的主要原因是因为你本质上试图做
Encoder[Base { type T }]
Run Code Online (Sandbox Code Playgroud)
不用说T
是什么类型。这类似于期望该函数编译 -
def foo[A] = implicitly[Encoder[List[A]]]
Run Code Online (Sandbox Code Playgroud)
您需要明确地细化您的类型。
解决这个问题的一种方法是使用模式Aux
。您不能使用典型的type Aux[S] = Base { type T = S }
,因为在尝试派生实例时,这不会给您提供余积( 和X
类Y
不能从类型别名扩展)。相反,我们可以通过创建另一个密封特征来绕过它Aux
,并让我们的案例类从中扩展。
只要您的所有案例类都从而Base.Aux
不是直接从扩展Base
,您就可以使用以下滥用.asInstanceOf
来安抚类型系统。
sealed trait Inner
case class I1(i: Int) extends Inner
case class I2(s: String) extends Inner
sealed trait Base { type T <: Inner }
object Base {
sealed trait Aux[S <: Inner] extends Base { type T = S }
implicit val encoder: Encoder[Base] = {
semiauto.deriveEncoder[Base.Aux[Inner]].asInstanceOf[Encoder[Base]]
}
implicit val decoder: Decoder[Base] = {
semiauto.deriveDecoder[Base.Aux[Inner]].asInstanceOf[Decoder[Base]]
}
}
val encoded = Encoder[Base].apply(Y(I1(1)))
val decoded = Decoder[Base].apply(encoded.hcursor)
Run Code Online (Sandbox Code Playgroud)
请注意,这很大程度上取决于您实际使用类型的方式。我想您不会依赖Encoder[Base]
直接调用,而是使用import io.circe.syntax._
并调用.asJson
扩展方法。在这种情况下,您可以依赖一个Encoder[Base.Aux[S]]
根据编码/解码的值推断的实例。像下面这样的东西可能足以满足您的用例,而无需诉诸.asInstanceOf
黑客。
implicit def encoder[S <: Inner : Encoder]: Encoder[Base.Aux[S]] = {
semiauto.deriveEncoder
}
Run Code Online (Sandbox Code Playgroud)
同样,这完全取决于您如何使用实例。我怀疑您实际上需要 中的类型成员Base
,如果您将其移至通用参数,那么事情会更简单,以便派生者可以为您计算出余积。
归档时间: |
|
查看次数: |
818 次 |
最近记录: |