gpt4 book ai didi

json - 使用鉴别器编码 ADT 案例类,即使键入为案例类

转载 作者:行者123 更新时间:2023-12-01 00:15:41 25 4
gpt4 key购买 nike

假设我在 Scala 中有一个 ADT:

sealed trait Base
case class Foo(i: Int) extends Base
case class Baz(x: String) extends Base

我想将此类型的值编码为如下所示的 JSON:
{ "Foo": { "i": 10000 }}
{ "Baz": { "x": "abc" }}

幸运的是,这正是编码 circe 的通用派生提供的!
scala> import io.circe.generic.auto._, io.circe.syntax._
import io.circe.generic.auto._
import io.circe.syntax._

scala> val foo: Base = Foo(10000)
foo: Base = Foo(10000)

scala> val baz: Base = Baz("abc")
baz: Base = Baz(abc)

scala> foo.asJson.noSpaces
res0: String = {"Foo":{"i":10000}}

scala> baz.asJson.noSpaces
res1: String = {"Baz":{"x":"abc"}}

问题在于,circe 使用的编码器取决于我们正在编码的表达式的静态类型。这意味着如果我们尝试直接解码其中一个案例类,我们会丢失判别器:
scala> Foo(10000).asJson.noSpaces
res2: String = {"i":10000}

scala> Baz("abc").asJson.noSpaces
res3: String = {"x":"abc"}

…但我想要 Base即使静态类型为 Foo 也进行编码.我知道我可以为所有案例类定义显式实例,但在某些情况下我可能有很多,我不想枚举它们。

(请注意,这是一个出现过几次的问题—— e.g. here。)

最佳答案

通过为只委托(delegate)给 Base 的基本类型的子类型定义一个实例,可以相当简单地做到这一点。解码器:

import cats.syntax.contravariant._
import io.circe.ObjectEncoder, io.circe.generic.semiauto.deriveEncoder

sealed trait Base
case class Foo(i: Int) extends Base
case class Baz(x: String) extends Base

object Base {
implicit val encodeBase: ObjectEncoder[Base] = deriveEncoder
}

object BaseEncoders {
implicit def encodeBaseSubtype[A <: Base]: ObjectEncoder[A] = Base.encodeBase.narrow
}

它按预期工作:
scala> import BaseEncoders._
import BaseEncoders._

scala> import io.circe.syntax._
import io.circe.syntax._

scala> Foo(10000).asJson.noSpaces
res0: String = {"Foo":{"i":10000}}

scala> (Foo(10000): Base).asJson.noSpaces
res1: String = {"Foo":{"i":10000}}

不幸的是 encodeBaseSubtype无法在 Base 中定义伴生对象,从那时起它将被 deriveEncoder 拾取宏,导致循环定义(和堆栈溢出等)。我想我在某个时候为这个问题想出了一种可怕的解决方法——如果我这样做了,我会尝试找到它并将其作为另一个答案发布。

关于json - 使用鉴别器编码 ADT 案例类,即使键入为案例类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52856478/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com