【问题标题】:How to use circe with generic case class that extends a sealed trait如何将 circe 与扩展密封特征的通用案例类一起使用
【发布时间】:2022-07-21 04:58:01
【问题描述】:

我有这个最小的例子,我想为通用案例类A[T]创建具有circe半自动派生的编码器/解码器

import io.circe.{Decoder, Encoder}
import io.circe.generic.semiauto._
import io.circe.syntax._

sealed trait MyTrait
object MyTrait {
  implicit val encoder: Encoder[MyTrait] = deriveEncoder
  implicit val decoder: Decoder[MyTrait] = deriveDecoder
}

case class A[T](value: T) extends MyTrait
object A {
  implicit def encoder[T: Encoder]: Encoder[A[T]] = deriveEncoder
  implicit def decoder[T: Decoder]: Decoder[A[T]] = deriveDecoder
}

此代码无法编译,而是输出此错误

could not find Lazy implicit value of type io.circe.generic.encoding.DerivedAsObjectEncoder[A]

解码器也是如此

我在这里做错了什么,我怎样才能让它工作?

【问题讨论】:

标签: scala generics circe


【解决方案1】:

问题很少。一种是MyTraitEncoder/Decoder 将尝试将编码/解码分派到子类型的编解码器中——因为特性是密封的,所有可能的特性都是已知的,因此可以通过编译器获得这样的列表。

但是

虽然MyTrait trait 不接受类型参数,但它的唯一实现 A 接受。这基本上把它变成了一种存在类型。

val myTrait: MyTrait = A(10)

myTrait match {
  case A(x) =>
    // x is of unknown type, at best you could use runtime reflection
    // but codecs are generated with compile time reflection
}

即使你想手动制作这些编解码器,你也没有办法

object Scope {

  def aEncoder[T: Encoder]: Encoder[A[T]] = ...

  val myTraitEncoder: Encoder[MyTrait] = {
    case A(value) =>
      // value is of unknown type, how to decide what Encoder[T]
      // pass into aEncoder?
  }
}

出于类似原因,您无法手动实现Decoder

为了能够手动实现编解码器(这是能够生成编解码器的一种先决条件),您只能在进入子类时删除类型参数,切勿添加它们。

sealed trait MyTrait[T]
case class A[T](value: T) extends MyTrait[T]

这将可以知道TA 中的类型,因为它会从MyTrait 传递,因此您可以手动编写您的编解码器并且它们会工作。

另一个问题是 ADT 的可靠生成通常需要一些配置,例如是否使用歧视场。这是由circe-generic-extras 提供的。一旦你使用它,(半)自动推导是可能的:

import io.circe.{Decoder, Encoder}
import io.circe.generic.extras.Configuration
import io.circe.generic.extras.semiauto._
import io.circe.syntax._

// can be used to tweak e.g. discriminator field name
implicit val config: Configuration = Configuration.default

sealed trait MyTrait[T]
object MyTrait {
  implicit def encoder[T: Encoder]: Encoder[MyTrait[A]] = deriveEncoder
  implicit def decoder[T: Decoder]: Decoder[MyTrait[A]] = deriveDecoder
}

case class A[T](value: T) extends MyTrait[T]
object A {
  implicit def encoder[T: Encoder]: Encoder[A[T]] = deriveEncoder
  implicit def decoder[T: Decoder]: Decoder[A[T]] = deriveDecoder
}

【讨论】:

    猜你喜欢
    • 2018-10-31
    • 2020-03-23
    • 2021-03-08
    • 2020-09-10
    • 2016-08-19
    • 2020-03-30
    • 2016-08-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多