在 Circe 中扩展 AutoDerivation 不起作用

Extending AutoDerivation in Circe does not work

我的问题涉及 mixel 提供的第二种解决方案:

请注意,Circe 中名为 Auto 的特征在当前版本的 Circe 中已重命名为 AutoDerivation。

我正在使用 mixel 在他的 Whosebug 解决方案中提供的解决方案,但无法使其正常工作。我已经尝试过将我的 Circe 版本更新到最新版本并确保导入了 Macro Paradise 插件,但仍然没有成功。

这是我的代码。第一个是它自己的文件,称为 CirceGeneric。

import io.circe._
import io.circe.parser._
import io.circe.generic.extras._

object CirceGeneric {
  trait JsonEncoder[T] {
    def apply(in: T): Json
  }

  trait JsonDecoder[T] {
    def apply(s: String): Either[Error, T]
  }

  object CirceEncoderProvider {
    def apply[T: Encoder]: JsonEncoder[T] = new JsonEncoder[T] {
      def apply(in: T) = Encoder[T].apply(in)
    }
  }

  object CirceDecoderProvider {
    def apply[T: Decoder]: JsonDecoder[T] = new JsonDecoder[T] {
      def apply(s: String) = decode[T](s)
    }
  }
}

object Generic extends AutoDerivation {
  import CirceGeneric._

  implicit def encoder[T: Encoder]: JsonEncoder[T] = CirceEncoderProvider[T]
  implicit def decoder[T: Decoder]: JsonDecoder[T] = CirceDecoderProvider[T]

}

第二种是使用Akka函数responseAs进行单元测试的方法。该方法出现在名为 BaseServiceTest 的 class 中。

  def responseTo[T]: T = {
    def response(s: String)(implicit d: JsonDecoder[T]) = {
      d.apply(responseAs[String]) match {
        case Right(value) => value
        case Left(error) => throw new IllegalArgumentException(error.fillInStackTrace)
      }
    }
    response(responseAs[String])
  }

想法是将 responseAs[String](其中 returns 一个字符串)的结果转换为解码后的大小写 class。

代码未按预期运行。 Intellij 没有检测到任何缺失的隐式,但是当编译时间到来时,我遇到了问题。我应该提到 BaseServiceTest 文件包含 CirceGeneric._ 和 Generic._ 的导入,因此缺少导入语句不是问题。

[错误] [...]/BaseServiceTest.scala:59: 找不到参数 d 的隐含值: [...]CirceGeneric.JsonDecoder[T] [错误] 响应(responseAs[String])

从 Decoder[T] 到 JsonDecoder[T] 的隐式转换没有发生,或者没有创建 Decoder[T] 实例。无论哪种方式,都有问题。

您仍然需要 DecoderJsonDecoder 上下文绑定到 responseTo

def responseTo[T : Decoder]: T = ...

这是因为您的所有代码,实际上是链接答案中的 mixel 代码,都是关于从 Decoder 抽象为可用于 cross-library 的 JsonDecoder 特征支持。但是你仍然没有任何方法可以在没有底层 Decoder 实例的情况下构建一个。

现在,有一些方法可以为 circe.generics.auto 中包含的(例如)案例 class 自动生成 Decoder,但此时在您的代码中

def responseTo[T]: T = {
    def response(s: String)(implicit d: JsonDecoder[T]) = ...
    ...
}

您要求编译器能够为任意类型提供隐式 JsonDecoder(即,在您的设置中,Decoder)实例。正如链接问题的公认答案所解释的那样,这是不可能的。

您需要将隐式解析延迟到知道您正在处理的类型的程度 - 特别是,您可以为其提供一个 Decoder[T] 实例。

编辑:在您对您关于如果您不能为所有类型创建 JsonDecoders 的评论的回复中...

我对链接问题的理解是,他们试图抽象出 circe 库,以便允许换出 JSON 库实现。这是按如下方式完成的:

  • 添加 JsonDecoder 类型 class

  • 有一个包 json,其中包含隐含函数(使用 Circe),用于通过扩展包对象自动构造它们 AutoDerivation

  • 有外部代码仅引用 JsonDecoder 并在 json 包中导入隐式

然后所有 JSON 序列化和隐式解析都可以解决,而无需调用代码引用 io.circe,并且可以轻松切换 json/JsonDecoder 到另一个 JSON 库(如果需要)。但是您仍然必须使用 JsonDecoder 上下文绑定,并且仅限于使用可以构造此类隐式的类型。这不是每种类型。