简体   繁体   中英

Circe and Scala's Enumeration type

I'm trying to wrap my head around Circe.

So, here's the model I've been given:

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

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

I want to

a) encode this simple example to a JSON string

        val product = Product(id = "1234", gender = Gender.Female)

b) map the resulting JSON back onto the Product case class.


My own attempt didn't get me very far:

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

resulted in a compile time error

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

I've no idea why this exception is thrown and what the solution could look like. Maybe it's the usage of the Enumeration type? But, I'm only guessing.

Try defining your own encoders and decoders for the enum using:

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

something like:

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
}

These are needed because the automatic/semiautomatic derivers only work for hierarchies of sealed trait s and case classes as far as I know. The reason you see that error is because the derived codecs for Product will implicitly require encoders/decoders for the types of each of it's parameters. An encoder/decoder for String is a standard part of Circe, but you'll probably need to create ones for your own enumerations.

Have a look at enumeratum if you want to use enumerations with circe. You could then try something like this:

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

This should work with circe's automatic derivation for use with your case classes.

The accepted answer is deprecated ( circe 0.12.0 ).

Circe provides now these functions:

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

With the example:

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

For Scala 3 there is no solution today within Circe .

However there is a library that works nicely: circe-tagged-adt-codec

Here an example that works for me (the rest I do with semiautomatic derivation from Circe):

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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