简体   繁体   English

使用鉴别器编码ADT案例类,即使在键入案例类时也是如此

[英]Encoding ADT case classes with a discriminator, even when typed as the case class

Suppose I have a ADT in Scala: 假设我在Scala中有一个ADT:

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

I want to encode values of this type into the JSON that looks like the following: 我想将此类型的值编码为JSON,如下所示:

{ "Foo": { "i": 10000 }}
{ "Baz": { "x": "abc" }}

Which luckily is exactly the encoding circe's generic derivation provides! 幸运的是,编码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"}}

The problem is that the encoder circe uses depends on the static type of the expression we're encoding. 问题是编码器circe使用取决于我们编码的表达式的静态类型。 This means that if we try to decode one of the case classes directly, we lose the discriminator: 这意味着如果我们尝试直接解码其中一个案例类,我们将失去鉴别器:

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

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

…but I want the Base encoding even when the static type is Foo . ...但是即使静态类型是Foo我也想要Base编码。 I know I can define explicit instances for all of the case classes, but in some cases I might have a lot of them, and I don't want to have to enumerate them. 我知道我可以为所有案例类定义显式实例,但在某些情况下我可能有很多它们,我不想要枚举它们。

(Note that this is a question that's come up a few times— eg here .) (请注意,这是一个问题,有几次出现 - 例如这里 。)

It is possible to do this fairly straightforwardly by defining an instance for subtypes of the base type that just delegates to the Base decoder: 通过定义仅委托给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
}

It works as expected: 它按预期工作:

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}}

Unfortunately encodeBaseSubtype can't be defined in the Base companion object, since then it'd be picked up by the deriveEncoder macro, resulting in a cyclic definition (and stack overflows, etc.). 遗憾的是,无法在Base伴随对象中定义encodeBaseSubtype ,因为它会被deriveEncoder宏拾取,从而导致循环定义(以及堆栈溢出等)。 I think I came up with a kind of horrible workaround for this problem at some point—I'll try to find it and post it as another answer if I do. 我想我在某个时候想出了一个可怕的解决方法 - 我会尝试找到它并将其作为另一个答案发布,如果我这样做的话。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM