【问题标题】:Extending AutoDerivation in Circe does not work在 Circe 中扩展 AutoDerivation 不起作用
【发布时间】:2019-11-19 13:25:14
【问题描述】:

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

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

我正在使用 mixel 在他的 StackOverflow 解决方案中提供的解决方案,但无法使其正常工作。我已经尝试过将我的 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 的类中。

  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]的结果(返回一个字符串)转换成解码的case类。

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

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

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

【问题讨论】:

    标签: json scala circe


    【解决方案1】:

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

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

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

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

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

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

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

    编辑:在您回复您的评论时,如果您不能为所有类型创建 JsonDecoders 的意义何在...

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

    • 添加JsonDecoder类型类

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

    • 外部代码只引用JsonDecoder 并导入json 包中的隐式

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

    【讨论】:

    • 感谢您的周到回复。我的理解是 AutoDerivation trait 可以提供任意编码器和解码器,而无需在编译时给出必要的类型。否则,如果 mixel 已经在隐式范围内拥有解码器,为什么 mixel 还要麻烦创建另一个名为 JsonDecoder 的特征来实例化?恐怕您的答案是正确的,但我会再等几天,看看是否有人有替代答案,然后再接受您的答案作为首选回复。
    • @user6337 - 我已经编辑了我的答案,解释了你为什么要这样做。
    猜你喜欢
    • 2014-08-09
    • 2020-11-15
    • 2016-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-30
    相关资源
    最近更新 更多