为泛型ADT添加Numeric上下文边界后Circe编码器推导失败的排查与解决
问题:Circe自动推导Encoder失败(泛型类型约束引发)
背景代码
最初的代码可正常将ADT编码为JSON:
import io.circe.generic.extras.Configuration import io.circe.generic.extras.auto._ import io.circe.syntax._ implicit val Conf: Configuration = Configuration .default .withSnakeCaseConstructorNames .withSnakeCaseMemberNames .withDiscriminator("type") sealed trait Generator[T] case class UniformGenerator[T](min: T, max: T) extends Generator[T] case class EnumGenerator[T](values: Set[T]) extends Generator[T] sealed trait DataType case class IntType(generator: Generator[Int]) extends DataType case class StringType(generator: Generator[String]) extends DataType
执行测试代码:
println((IntType(UniformGenerator(1, 5)): DataType).asJson)
输出正常:
{ "generator" : { "min" : 1, "max" : 5, "type" : "uniform_generator" }, "type" : "int_type" }
问题复现
当为UniformGenerator添加泛型类型约束[T: Numeric]后:
case class UniformGenerator[T: Numeric](min: T, max: T) extends Generator[T]
编译报错:
could not find implicit value for parameter encoder: io.circe.Encoder[DataType]
println((IntType(UniformGenerator(1, 5)): DataType).asJson)
原因分析
Circe的auto自动编码器推导依赖Shapeless的Generic机制,要求case class的构造参数能被自动解析出对应的Encoder实例。当UniformGenerator添加[T: Numeric]约束后,等价于构造函数中增加了隐式参数implicit ev: Numeric[T],而Circe自动推导无法处理这个额外的隐式依赖,导致无法推导出Generator[Int]的Encoder,进而无法生成DataType的Encoder。
解决方法
方法1:手动编写UniformGenerator的Encoder
利用Numeric实例和现有Encoder,手动实现编码器逻辑:
import io.circe.Encoder import io.circe.generic.extras.semiauto._ // 为带Numeric约束的Generator推导Encoder implicit def generatorEncoder[T: Encoder: Numeric]: Encoder[Generator[T]] = deriveEncoder[Generator[T]] // 为DataType推导Encoder implicit val dataTypeEncoder: Encoder[DataType] = deriveEncoder[DataType]
方法2:使用半自动推导替代自动推导
放弃auto._导入,显式为每个ADT定义Encoder,明确处理隐式依赖:
// 移除import io.circe.generic.extras.auto._ import io.circe.generic.extras.semiauto._ // 为UniformGenerator定义Encoder implicit def uniformGeneratorEncoder[T: Encoder: Numeric]: Encoder[UniformGenerator[T]] = deriveEncoder[UniformGenerator[T]] // 为EnumGenerator定义Encoder implicit def enumGeneratorEncoder[T: Encoder]: Encoder[EnumGenerator[T]] = deriveEncoder[EnumGenerator[T]] // 为Generator定义Encoder implicit def generatorEncoder[T: Encoder: Numeric]: Encoder[Generator[T]] = deriveEncoder[Generator[T]] // 为DataType相关类型定义Encoder implicit def intTypeEncoder: Encoder[IntType] = deriveEncoder[IntType] implicit def stringTypeEncoder: Encoder[StringType] = deriveEncoder[StringType] implicit val dataTypeEncoder: Encoder[DataType] = deriveEncoder[DataType]
内容的提问来源于stack exchange,提问作者sanyi14ka
相关产品推荐
相关产品推荐

