Search code examples
jsonscalacirce

Why isn't my discriminating field being added to my encoded case object using circe?


I have the following code, which I expect to print the type of object being encoded, but it only prints an empty object:

import cats.syntax.functor._
import io.circe.generic.auto._
import io.circe.generic.extras.Configuration
import io.circe.syntax._
import io.circe.{Decoder, Encoder}

object Main extends App {

  implicit val customConfig: Configuration = 
    Configuration.default.withDefaults.withDiscriminator("type")

  sealed trait Foo

  final case object Bar extends Foo
  final case object Boo extends Foo

  implicit val encodeEvent: Encoder[Foo] = Encoder.instance {
    case Bar => Bar.asJson
    case Boo => Boo.asJson
  }

  implicit val decodeEvent: Decoder[Foo] =
    List[Decoder[Foo]](
      Decoder[Bar.type].widen,
      Decoder[Boo.type].widen,
    ).reduceLeft(_ or _)

   val bar = Bar

   println((bar: Foo).asJson.noSpaces) // {}
}

Why isn't the Configuration being applied to the encoding of my Foo?


Solution

  • The following does work:

    import cats.syntax.functor._
    import io.circe.generic.extras.semiauto._
    import io.circe.generic.extras.Configuration
    import io.circe.syntax._
    import io.circe.{Decoder, Encoder}
    
    object Main extends App {
    
      implicit val customConfig: Configuration =
        Configuration.default.withDefaults.withDiscriminator("type")
    
      sealed trait Foo
    
      final case object Bar extends Foo
      final case object Boo extends Foo
    
      implicit val eBar: Encoder[Bar.type] = deriveEncoder[Bar.type]
      implicit val eBoo: Encoder[Boo.type] = deriveEncoder[Boo.type]
    
    
      implicit val dBar: Decoder[Bar.type] = deriveDecoder[Bar.type]
      implicit val dBoo: Decoder[Boo.type] = deriveDecoder[Boo.type]
    
      implicit val encodeEvent: Encoder[Foo] = deriveEncoder[Foo]
    
      implicit val decodeEvent: Decoder[Foo] =
        List[Decoder[Foo]](
          Decoder[Bar.type].widen,
          Decoder[Boo.type].widen,
        ).reduceLeft(_ or _)
    
      val foo: Foo = Boo: Foo
    
      println(foo.asJson.noSpaces) // {"type":"Boo"}
    }
    

    The things to note are:

    1. Configuration is (to the best of my knowledge) for io.circe.generic.extras.semiauto and not for io.circe.generic.auto
    2. You had supplied an Encoder and did not let it (semi) auto derive. While the configuration is for the (semi) auto derivation.