Circe: Case class with Enumeration is not converted

Created on 23 May 2016  路  5Comments  路  Source: circe/circe

I'm trying to convert a case class that has a Enumeration as a argument

import io.circe._, io.circe.generic.auto._, io.circe.jawn._, io.circe.syntax._
object WeekDay extends Enumeration {
  val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}
case class test1(day: WeekDay.Value)
case class test2(day: String)

While test2("Mon").asJson works as expected:

{
  "day" : "Mon"
}

But: test1(WeekDay.Mon).asJson results in:

:27: error: could not find implicit value for parameter encoder: io.circe.Encoder[test]
test(WeekDay.Mon).asJson

Is there a way to work around this or is this a bug?

enhancement question

Most helpful comment

I'm not seeing anywhere in the project where basic scala Enumerations have a provided Encoder/Decoder instance. However, following how the json4s handled this I came up with the following

class EnumTranscoder[E <: Enumeration](enum: E) extends Decoder[E#Value] with Encoder[E#Value] {
  override def apply(c: HCursor): Result[E#Value] = Decoder.decodeString.map(enum.withName).apply(c)

  override def apply(a: E#Value): Json = Encoder.encodeString.apply(a.toString)
}

All 5 comments

I'm not seeing anywhere in the project where basic scala Enumerations have a provided Encoder/Decoder instance. However, following how the json4s handled this I came up with the following

class EnumTranscoder[E <: Enumeration](enum: E) extends Decoder[E#Value] with Encoder[E#Value] {
  override def apply(c: HCursor): Result[E#Value] = Decoder.decodeString.map(enum.withName).apply(c)

  override def apply(a: E#Value): Json = Encoder.encodeString.apply(a.toString)
}

This isn't exactly a bug, but it is at least arguably a missing feature. There are lots of reasons not to use Scala's Enumeration, and circe (and Argonaut before it) has never provided built-in support for them.

If there's demand, though, I definitely think we could consider a PR adding something like the instances in @dhensche's comment to Encoder and Decoder.

There are certainly reasons, but sometimes you don't have control over these things, so an integrated enumeration encoder would definitely be very useful.

The class-based version above didn't work for me, but this one works (similarly used in Play JSON):

  def enumDecoder[E <: Enumeration](enum: E) = new Decoder[E#Value] {
    override def apply(c: HCursor): Result[E#Value] = Decoder.decodeString.map(str => enum.withName(str)).apply(c)
  }

Where would this have to go in Circe, directly into Encoder and Decoder? (I'm pretty new to it.) I could create a PR.

Yes, we'd want them to be in the companion objects so that they get made available without additional imports. Thanks, @mariussoutier!

Was this page helpful?
0 / 5 - 0 ratings