Circe和Scala的枚举类型

15

我正在努力理解Circe。

所以,这是我收到的模型:

object Gender extends Enumeration {
     type Gender = Value
     val Male, Female, Unisex, Unknown = Value
}

case class Product(id: String, gender: Gender.Value)

我想将以下示例编码为 JSON

        val product = Product(id = "1234", gender = Gender.Female)
b) 将生成的JSON映射回Product case类。

我的尝试并没有让我走得太远:

  object JsonProtocol {
      implicit val productDecoder: Decoder[Product] = deriveDecoder
      implicit val productEncoder: Encoder[Product] = deriveEncoder
  }

结果是编译时错误。

   Error:(52, 49) could not find Lazy implicit value of type io.circe.generic.decoding.DerivedDecoder[A]
   implicit val productDecoder: Decoder[Product] = deriveDecoder
                                            ^

我不知道为什么会抛出这个异常,也不知道解决方案是什么。也许是枚举类型的使用有问题?

4个回答

15

尝试使用以下方法定义您自己的编码器和解码器枚举:

Decoder.enumDecoder[E <: Enumeration](enum: E)
Encoder.enumEncoder[E <: Enumeration](enum: E)

类似这样的内容:

object JsonProtocol {
  implicit val genderDecoder: Decoder[Gender.Value] = Decoder.enumDecoder(Gender)
  implicit val genderEncoder: Encoder[Gender.Value] = Encoder.enumEncoder(Gender)
  implicit val productDecoder: Decoder[Product] = deriveDecoder
  implicit val productEncoder: Encoder[Product] = deriveEncoder
}

据我所知,自动/半自动驱动程序仅适用于层次结构的sealed traitcase class。这就是为什么您会看到该错误的原因,因为Product的派生编解码器将隐含地要求每个参数的类型的编码器/解码器。 String的编码器/解码器是Circe的标准部分,但您可能需要为自己的枚举创建它们。


3
自circe 0.12.0起,enumEncoder/enumDecoder已被弃用,推荐使用encodeEnumeration/decodeEnumeration。 - CPS

15

所接受的答案已被弃用(circe 0.12.0)。

Circe 现在提供这些函数:

Decoder.decodeEnumeration[E <: Enumeration](enum: E)
Encoder.encodeEnumeration[E <: Enumeration](enum: E)

使用以下示例:

implicit val genderDecoder: Decoder[Gender.Value] = Decoder.decodeEnumeration(Gender)
implicit val genderEncoder: Encoder[Gender.Value] = Encoder.encodeEnumeration(Gender)

1
谢谢。这对于Scala 2非常有效;但不幸的是,我在https://github.com/circe/circe/issues/2006中尝试将其交叉编译到Scala 3时遇到了问题。 - Hartmut Pfarr
没错 - 我添加了一个使用Scala 3的答案:https://dev59.com/IVgQ5IYBdhLWcg3w43-V#73089261 - pme

11

如果您想在circe中使用枚举,请查看enumeratum。然后您可以尝试类似这样的代码:

import enumeratum._

sealed trait Gender extends EnumEntry

case object Gender extends CirceEnum[Gender] with Enum[Gender] {

  case object Male extends Gender
  case object Female extends Gender
  case object Unisex extends Gender
  case object Unknown extends Gender

  val values = findValues
}

Gender.values.foreach { gender =>
    assert(gender.asJson == Json.fromString(gender.entryName))
}

您可以使用circe的自动派生功能来处理您的case class。


3

对于 Scala 3,现在在 Circe 的版本 0.14.5 中有解决方案。

有一篇很好的博客解释了这个问题:https://scalajobs.com/blog/enum-serialization-in-scala/

博客中的代码:

import io.circe.Codec
import io.circe.derivation.Configuration

given Configuration = Configuration.default
    .withDiscriminator("type")
    .withTransformConstructorNames(_.toUpperCase)​
enum Role {
  case Reader(subscription: Subscription)
  case Editor(profileBio: String, favoriteFont: String)
  case Admin
}
object Role {    ​
  given Codec[Role] = Codec.AsObject.derivedConfigured
}

这将创建:

{"type":"TESTER","subscription":"GENESIS"}

如果您只有简单的枚举,可以使用以下方法:
import io.circe.derivation.{Configuration, ConfiguredEnumCodec}

given Configuration = Configuration.default
enum TestOverrideType derives ConfiguredEnumCodec:
  case Exists, NotExists, IsEquals, HasSize

这将创建:

"NotExists"

感谢Lasering的帮助

我的早期回答:

对于Scala 3,目前在Circe中没有解决方案。

但是有一个很好用的库:circe-tagged-adt-codec

这里有一个适合我的例子(其余部分我使用Circe的半自动派生完成):

enum TestOverrideType derives JsonTaggedAdt.PureEncoder:
  case Exists, NotExists, IsEquals, HasSize

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接